From 5ef106b9fbfcaccb0d22b84feebc79b59ff7eea0 Mon Sep 17 00:00:00 2001 From: joe-king-sh Date: Fri, 8 Jul 2022 15:16:01 +0900 Subject: [PATCH 01/92] feat(core): add a description parameter for the NestedStackProps (#20930) This PR adds a description parameter for the NestedStackProps. Fixes #16337 ---- ### All Submissions: * [x] Have you followed the guidelines in our [Contributing guide?](https://github.com/aws/aws-cdk/blob/main/CONTRIBUTING.md) ### Adding new Unconventional Dependencies: * [ ] This PR adds new unconventional dependencies following the process described [here](https://github.com/aws/aws-cdk/blob/main/CONTRIBUTING.md/#adding-new-unconventional-dependencies) ### New Features * [ ] Have you added the new feature to an [integration test](https://github.com/aws/aws-cdk/blob/main/INTEGRATION_TESTS.md)? * [ ] Did you use `yarn integ` to deploy the infrastructure and generate the snapshot (i.e. `yarn integ` without `--dry-run`)? *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- .../aws-cloudformation/test/integ.nested-stack.ts | 4 +++- packages/@aws-cdk/core/README.md | 10 ++++++++++ packages/@aws-cdk/core/lib/nested-stack.ts | 8 ++++++++ packages/@aws-cdk/core/test/nested-stack.test.ts | 9 +++++++++ packages/aws-cdk-lib/README.md | 10 ++++++++++ 5 files changed, 40 insertions(+), 1 deletion(-) diff --git a/packages/@aws-cdk/aws-cloudformation/test/integ.nested-stack.ts b/packages/@aws-cdk/aws-cloudformation/test/integ.nested-stack.ts index 56beb537969c8..c5f8227f9796b 100644 --- a/packages/@aws-cdk/aws-cloudformation/test/integ.nested-stack.ts +++ b/packages/@aws-cdk/aws-cloudformation/test/integ.nested-stack.ts @@ -12,6 +12,7 @@ interface MyNestedStackProps { readonly siblingTopic?: sns.Topic; // a topic defined in a sibling nested stack readonly topicCount: number; readonly topicNamePrefix: string; + readonly description?: string; } class MyNestedStack extends NestedStack { @@ -22,6 +23,7 @@ class MyNestedStack extends NestedStack { parameters: { [topicNamePrefixLogicalId]: props.topicNamePrefix, // pass in a parameter to the nested stack }, + description: props.description, }); const topicNamePrefixParameter = new CfnParameter(this, 'TopicNamePrefix', { type: 'String' }); @@ -57,7 +59,7 @@ class MyTestStack extends Stack { const queue = new sqs.Queue(this, 'SubscriberQueue'); new MyNestedStack(this, 'NestedStack1', { topicCount: 3, topicNamePrefix: 'Prefix1', subscriber: queue }); - new MyNestedStack(this, 'NestedStack2', { topicCount: 2, topicNamePrefix: 'Prefix2' }); + new MyNestedStack(this, 'NestedStack2', { topicCount: 2, topicNamePrefix: 'Prefix2', description: 'This is secound nested stack.' }); } } diff --git a/packages/@aws-cdk/core/README.md b/packages/@aws-cdk/core/README.md index 773c2802a839b..215365c730108 100644 --- a/packages/@aws-cdk/core/README.md +++ b/packages/@aws-cdk/core/README.md @@ -995,6 +995,16 @@ const stack = new Stack(app, 'StackName', { By default, termination protection is disabled. +### Description + +You can add a description of the stack in the same way as `StackProps`. + +```ts +const stack = new Stack(app, 'StackName', { + description: 'This is a description.', +}); +``` + ### CfnJson `CfnJson` allows you to postpone the resolution of a JSON blob from diff --git a/packages/@aws-cdk/core/lib/nested-stack.ts b/packages/@aws-cdk/core/lib/nested-stack.ts index ea24698ac2b28..3adb30e677800 100644 --- a/packages/@aws-cdk/core/lib/nested-stack.ts +++ b/packages/@aws-cdk/core/lib/nested-stack.ts @@ -68,6 +68,13 @@ export interface NestedStackProps { * @default RemovalPolicy.DESTROY */ readonly removalPolicy?: RemovalPolicy; + + /** + * A description of the stack. + * + * @default - No description. + */ + readonly description?: string; } /** @@ -112,6 +119,7 @@ export class NestedStack extends Stack { super(scope, id, { env: { account: parentStack.account, region: parentStack.region }, synthesizer: new NestedStackSynthesizer(parentStack.synthesizer), + description: props.description, }); this._parentStack = parentStack; diff --git a/packages/@aws-cdk/core/test/nested-stack.test.ts b/packages/@aws-cdk/core/test/nested-stack.test.ts index b1508e8bf148f..697253fe5f7fd 100644 --- a/packages/@aws-cdk/core/test/nested-stack.test.ts +++ b/packages/@aws-cdk/core/test/nested-stack.test.ts @@ -22,4 +22,13 @@ describe('nested-stack', () => { }, }); }); + test('a nested-stack has a description in templateOptions.', () => { + const description = 'This is a description.'; + const stack = new Stack(); + var nestedStack = new NestedStack(stack, 'MyNestedStack', { + description, + }); + + expect(nestedStack.templateOptions.description).toEqual(description); + }); }); \ No newline at end of file diff --git a/packages/aws-cdk-lib/README.md b/packages/aws-cdk-lib/README.md index a620a2aecc4ad..a8e26848a6112 100644 --- a/packages/aws-cdk-lib/README.md +++ b/packages/aws-cdk-lib/README.md @@ -1026,6 +1026,16 @@ const stack = new Stack(app, 'StackName', { By default, termination protection is disabled. +### Description + +You can add a description of the stack in the same way as `StackProps`. + +```ts +const stack = new Stack(app, 'StackName', { + description: 'This is a description.', +}); +``` + ### CfnJson `CfnJson` allows you to postpone the resolution of a JSON blob from From 2936ed7976c245d4b3d353924cbd3a3222702a15 Mon Sep 17 00:00:00 2001 From: Michael Krauklis Date: Fri, 8 Jul 2022 02:52:56 -0400 Subject: [PATCH 02/92] docs(ecs-patterns): make property descriptions more clear (#19404) ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- .../lib/base/application-load-balanced-service-base.ts | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/packages/@aws-cdk/aws-ecs-patterns/lib/base/application-load-balanced-service-base.ts b/packages/@aws-cdk/aws-ecs-patterns/lib/base/application-load-balanced-service-base.ts index 73b5ff45b6c3b..c48f7bef7b59a 100644 --- a/packages/@aws-cdk/aws-ecs-patterns/lib/base/application-load-balanced-service-base.ts +++ b/packages/@aws-cdk/aws-ecs-patterns/lib/base/application-load-balanced-service-base.ts @@ -89,7 +89,8 @@ export interface ApplicationLoadBalancedServiceBaseProps { * * @default - No certificate associated with the load balancer, if using * the HTTP protocol. For HTTPS, a DNS-validated certificate will be - * created for the load balancer's specified domain name. + * created for the load balancer's specified domain name if a domain name + * and domain zone are specified. */ readonly certificate?: ICertificate; @@ -105,8 +106,8 @@ export interface ApplicationLoadBalancedServiceBaseProps { /** * The protocol for connections from clients to the load balancer. * The load balancer port is determined from the protocol (port 80 for - * HTTP, port 443 for HTTPS). A domain name and zone must be also be - * specified if using HTTPS. + * HTTP, port 443 for HTTPS). If HTTPS, either a certificate or domain + * name and domain zone must also be specified. * * @default HTTP. If a certificate is specified, the protocol will be * set by default to HTTPS. From 95103b46f0ae0e6117e36c6be180288ef8d41ab9 Mon Sep 17 00:00:00 2001 From: Chris Yang Date: Fri, 8 Jul 2022 15:30:05 +0800 Subject: [PATCH 03/92] docs(ecs): add an example for AsgCapacityProvider with EC2 LaunchTemplate (#21047) This pull request offer an example to resolve #20870 for AutoScalingGroup uses EC2 LaunchTemplate within ecs cluster. Closes #20870 --- packages/@aws-cdk/aws-ecs/README.md | 30 +++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/packages/@aws-cdk/aws-ecs/README.md b/packages/@aws-cdk/aws-ecs/README.md index 71a1c4e91c133..7f6a0766253fe 100644 --- a/packages/@aws-cdk/aws-ecs/README.md +++ b/packages/@aws-cdk/aws-ecs/README.md @@ -167,6 +167,36 @@ const autoScalingGroup = new autoscaling.AutoScalingGroup(this, 'ASG', { }); ``` +To use `LaunchTemplate` with `AsgCapacityProvider`, make sure to specify the `userData` in the `LaunchTemplate`: + +```ts +const launchTemplate = new ec2.LaunchTemplate(this, 'ASG-LaunchTemplate', { + instanceType: new ec2.InstanceType('t3.medium'), + machineImage: ecs.EcsOptimizedImage.amazonLinux2(), + userData: ec2.UserData.forLinux(), +}); + +const autoScalingGroup = new autoscaling.AutoScalingGroup(this, 'ASG', { + vpc, + mixedInstancesPolicy: { + instancesDistribution: { + onDemandPercentageAboveBaseCapacity: 50, + }, + launchTemplate: launchTemplate, + }, +}); + +const cluster = new ecs.Cluster(this, 'Cluster', { vpc }); + +const capacityProvider = new ecs.AsgCapacityProvider(this, 'AsgCapacityProvider', { + autoScalingGroup, + machineImageType: ecs.MachineImageType.AMAZON_LINUX_2, +}); + +cluster.addAsgCapacityProvider(capacityProvider); +``` + + ### Bottlerocket [Bottlerocket](https://aws.amazon.com/bottlerocket/) is a Linux-based open source operating system that is From 588ddf1b509029c70eaf60d0cd852bdc834a3caa Mon Sep 17 00:00:00 2001 From: Peter Woodworth <44349620+peterwoodworth@users.noreply.github.com> Date: Fri, 8 Jul 2022 01:08:32 -0700 Subject: [PATCH 04/92] fix(route53): publichostedzone import returns IHostedZone instead of IPublicHostedZone (#21007) fixes #21004 ---- ### All Submissions: * [ ] Have you followed the guidelines in our [Contributing guide?](https://github.com/aws/aws-cdk/blob/main/CONTRIBUTING.md) ### Adding new Unconventional Dependencies: * [ ] This PR adds new unconventional dependencies following the process described [here](https://github.com/aws/aws-cdk/blob/main/CONTRIBUTING.md/#adding-new-unconventional-dependencies) ### New Features * [ ] Have you added the new feature to an [integration test](https://github.com/aws/aws-cdk/blob/main/INTEGRATION_TESTS.md)? * [ ] Did you use `yarn integ` to deploy the infrastructure and generate the snapshot (i.e. `yarn integ` without `--dry-run`)? *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- .../aws-elasticloadbalancingv2/test/nlb/load-balancer.test.ts | 2 +- packages/@aws-cdk/aws-route53/lib/hosted-zone.ts | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/@aws-cdk/aws-elasticloadbalancingv2/test/nlb/load-balancer.test.ts b/packages/@aws-cdk/aws-elasticloadbalancingv2/test/nlb/load-balancer.test.ts index caf0b6ed751c5..cb04a4c9058fb 100644 --- a/packages/@aws-cdk/aws-elasticloadbalancingv2/test/nlb/load-balancer.test.ts +++ b/packages/@aws-cdk/aws-elasticloadbalancingv2/test/nlb/load-balancer.test.ts @@ -59,7 +59,7 @@ describe('tests', () => { const endpointService = new ec2.VpcEndpointService(stack, 'EndpointService', { vpcEndpointServiceLoadBalancers: [nlb] }); // WHEN - const importedPHZ = route53.PublicHostedZone.fromHostedZoneAttributes(stack, 'MyPHZ', { + const importedPHZ = route53.PublicHostedZone.fromPublicHostedZoneAttributes(stack, 'MyPHZ', { hostedZoneId: 'sampleid', zoneName: 'MyZone', }); diff --git a/packages/@aws-cdk/aws-route53/lib/hosted-zone.ts b/packages/@aws-cdk/aws-route53/lib/hosted-zone.ts index e58070474cc6d..c7cd541703c57 100644 --- a/packages/@aws-cdk/aws-route53/lib/hosted-zone.ts +++ b/packages/@aws-cdk/aws-route53/lib/hosted-zone.ts @@ -250,8 +250,8 @@ export class PublicHostedZone extends HostedZone implements IPublicHostedZone { * @param id the logical name of this Construct * @param attrs the PublicHostedZoneAttributes (hosted zone ID and hosted zone name) */ - public static fromPublicHostedZoneAttributes(scope: Construct, id: string, attrs: PublicHostedZoneAttributes): IHostedZone { - class Import extends Resource implements IHostedZone { + public static fromPublicHostedZoneAttributes(scope: Construct, id: string, attrs: PublicHostedZoneAttributes): IPublicHostedZone { + class Import extends Resource implements IPublicHostedZone { public readonly hostedZoneId = attrs.hostedZoneId; public readonly zoneName = attrs.zoneName; public get hostedZoneArn(): string { From 8e6a387b2f8502a9a1c861d5bd1c5c7b0d3ada54 Mon Sep 17 00:00:00 2001 From: Jim Nicholls Date: Fri, 8 Jul 2022 19:10:51 +1000 Subject: [PATCH 05/92] fix(cli): format of tags in cdk.json is not validated (#21050) If tags is present in cdk.json, validate that it is an array of objects, and each object has a Tag string and a Value string. If tags is not structurally valid `cdk bootstrap` and `cdk deploy` fail with an error. `tags must be an array of { Tag: string, Value: string } objects` There is no attempt to validate the strings of each Tag and Value beyond that they are strings. closes #20854 ---- ### All Submissions: * [x] Have you followed the guidelines in our [Contributing guide?](https://github.com/aws/aws-cdk/blob/main/CONTRIBUTING.md) *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- packages/aws-cdk/lib/cli.ts | 5 ++-- packages/aws-cdk/lib/util/tags.ts | 20 ++++++++++++++++ packages/aws-cdk/test/util/tags.test.ts | 31 +++++++++++++++++++++++++ 3 files changed, 54 insertions(+), 2 deletions(-) create mode 100644 packages/aws-cdk/lib/util/tags.ts create mode 100644 packages/aws-cdk/test/util/tags.test.ts diff --git a/packages/aws-cdk/lib/cli.ts b/packages/aws-cdk/lib/cli.ts index 4533125708409..5aaedcc7e5759 100644 --- a/packages/aws-cdk/lib/cli.ts +++ b/packages/aws-cdk/lib/cli.ts @@ -23,6 +23,7 @@ import { data, debug, error, print, setLogLevel, setCI } from '../lib/logging'; import { displayNotices, refreshNotices } from '../lib/notices'; import { Command, Configuration, Settings } from '../lib/settings'; import * as version from '../lib/version'; +import { validateTags } from './util/tags'; // https://github.com/yargs/yargs/issues/1929 // https://github.com/evanw/esbuild/issues/1492 @@ -434,7 +435,7 @@ async function initCommandLine() { force: argv.force, toolkitStackName: toolkitStackName, execute: args.execute, - tags: configuration.settings.get(['tags']), + tags: validateTags(configuration.settings.get(['tags'])), terminationProtection: args.terminationProtection, parameters: { bucketName: configuration.settings.get(['toolkitBucket', 'bucketName']), @@ -464,7 +465,7 @@ async function initCommandLine() { notificationArns: args.notificationArns, requireApproval: configuration.settings.get(['requireApproval']), reuseAssets: args['build-exclude'], - tags: configuration.settings.get(['tags']), + tags: validateTags(configuration.settings.get(['tags'])), execute: args.execute, changeSetName: args.changeSetName, force: args.force, diff --git a/packages/aws-cdk/lib/util/tags.ts b/packages/aws-cdk/lib/util/tags.ts new file mode 100644 index 0000000000000..75d3277aff00f --- /dev/null +++ b/packages/aws-cdk/lib/util/tags.ts @@ -0,0 +1,20 @@ +import { Tag } from '../cdk-toolkit'; + +/** + * Throws an error if tags is neither undefined nor an array of Tags, + * as defined in cdk-toolkit.ts. + * + * It does not attempt to validate the tags themselves. It only validates + * that the objects in the array conform to the Tags interface. + */ +export function validateTags(tags: any): Tag[] | undefined { + const valid = tags === undefined || ( + Array.isArray(tags) && + tags.every(t => typeof(t.Tag) === 'string' && typeof(t.Value) === 'string') + ); + if (valid) { + return tags; + } else { + throw new Error('tags must be an array of { Tag: string, Value: string } objects'); + } +} diff --git a/packages/aws-cdk/test/util/tags.test.ts b/packages/aws-cdk/test/util/tags.test.ts new file mode 100644 index 0000000000000..9789eb449300d --- /dev/null +++ b/packages/aws-cdk/test/util/tags.test.ts @@ -0,0 +1,31 @@ +import { validateTags } from '../../lib/util/tags'; + +test('validateTags does not throw when given undefined', () => { + expect(validateTags(undefined)).toBeUndefined(); +}); + +test('validateTags does not throw when given an empty array', () => { + const tags: any = []; + expect(validateTags(tags)).toBe(tags); +}); + +test('validateTags does not throw when given array of Tag objects', () => { + const tags: any = [{ Tag: 'a', Value: 'b' }]; + expect(validateTags(tags)).toBe(tags); +}); + +test('validateTags throws when given an object', () => { + expect(() => validateTags({ a: 'b' })).toThrow('tags must be'); +}); + +test('validateTags throws when given an array of non-Tag objects', () => { + expect(() => validateTags([{ a: 'b' }])).toThrow('tags must be'); +}); + +test('validateTags throws when Tag is not a string', () => { + expect(() => validateTags([{ Tag: null, Value: 'b' }])).toThrow(); +}); + +test('validateTags throws when Value is not a string', () => { + expect(() => validateTags([{ Tag: 'a', Value: null }])).toThrow(); +}); From 68f09b7c6f8e6c1ddf13fdd4e116d12333d24c46 Mon Sep 17 00:00:00 2001 From: AWS CDK Automation <43080478+aws-cdk-automation@users.noreply.github.com> Date: Fri, 8 Jul 2022 12:17:51 +0200 Subject: [PATCH 06/92] feat(cfnspec): cloudformation spec v79.0.0 (#21053) --- packages/@aws-cdk/cfnspec/CHANGELOG.md | 55 ++++++++++++ packages/@aws-cdk/cfnspec/cfn.version | 2 +- .../000_cfn/000_official/000_AWS_ACMPCA.json | 2 +- .../000_cfn/000_official/000_AWS_APS.json | 2 +- .../000_official/000_AWS_AccessAnalyzer.json | 2 +- .../000_official/000_AWS_AmazonMQ.json | 2 +- .../000_cfn/000_official/000_AWS_Amplify.json | 2 +- .../000_AWS_AmplifyUIBuilder.json | 2 +- .../000_official/000_AWS_ApiGateway.json | 2 +- .../000_official/000_AWS_ApiGatewayV2.json | 2 +- .../000_official/000_AWS_AppConfig.json | 2 +- .../000_cfn/000_official/000_AWS_AppFlow.json | 2 +- .../000_official/000_AWS_AppIntegrations.json | 2 +- .../000_cfn/000_official/000_AWS_AppMesh.json | 2 +- .../000_official/000_AWS_AppRunner.json | 2 +- .../000_official/000_AWS_AppStream.json | 2 +- .../000_cfn/000_official/000_AWS_AppSync.json | 2 +- .../000_AWS_ApplicationAutoScaling.json | 2 +- .../000_AWS_ApplicationInsights.json | 2 +- .../000_cfn/000_official/000_AWS_Athena.json | 2 +- .../000_official/000_AWS_AuditManager.json | 2 +- .../000_official/000_AWS_AutoScaling.json | 2 +- .../000_AWS_AutoScalingPlans.json | 2 +- .../000_cfn/000_official/000_AWS_Backup.json | 2 +- .../000_cfn/000_official/000_AWS_Batch.json | 2 +- .../000_AWS_BillingConductor.json | 2 +- .../000_cfn/000_official/000_AWS_Budgets.json | 2 +- .../000_cfn/000_official/000_AWS_CE.json | 2 +- .../000_cfn/000_official/000_AWS_CUR.json | 2 +- .../000_official/000_AWS_Cassandra.json | 2 +- .../000_AWS_CertificateManager.json | 2 +- .../000_cfn/000_official/000_AWS_Chatbot.json | 2 +- .../000_cfn/000_official/000_AWS_Cloud9.json | 2 +- .../000_official/000_AWS_CloudFormation.json | 8 +- .../000_official/000_AWS_CloudFront.json | 2 +- .../000_official/000_AWS_CloudTrail.json | 2 +- .../000_official/000_AWS_CloudWatch.json | 2 +- .../000_official/000_AWS_CodeArtifact.json | 2 +- .../000_official/000_AWS_CodeBuild.json | 2 +- .../000_official/000_AWS_CodeCommit.json | 2 +- .../000_official/000_AWS_CodeDeploy.json | 2 +- .../000_AWS_CodeGuruProfiler.json | 2 +- .../000_AWS_CodeGuruReviewer.json | 2 +- .../000_official/000_AWS_CodePipeline.json | 2 +- .../000_official/000_AWS_CodeStar.json | 2 +- .../000_AWS_CodeStarConnections.json | 2 +- .../000_AWS_CodeStarNotifications.json | 2 +- .../000_cfn/000_official/000_AWS_Cognito.json | 2 +- .../000_cfn/000_official/000_AWS_Config.json | 2 +- .../000_cfn/000_official/000_AWS_Connect.json | 2 +- .../000_AWS_CustomerProfiles.json | 2 +- .../000_cfn/000_official/000_AWS_DAX.json | 2 +- .../000_cfn/000_official/000_AWS_DLM.json | 2 +- .../000_cfn/000_official/000_AWS_DMS.json | 2 +- .../000_official/000_AWS_DataBrew.json | 2 +- .../000_official/000_AWS_DataPipeline.json | 2 +- .../000_official/000_AWS_DataSync.json | 2 +- .../000_official/000_AWS_Detective.json | 2 +- .../000_official/000_AWS_DevOpsGuru.json | 2 +- .../000_AWS_DirectoryService.json | 2 +- .../000_cfn/000_official/000_AWS_DocDB.json | 2 +- .../000_official/000_AWS_DynamoDB.json | 2 +- .../000_cfn/000_official/000_AWS_EC2.json | 2 +- .../000_cfn/000_official/000_AWS_ECR.json | 2 +- .../000_cfn/000_official/000_AWS_ECS.json | 2 +- .../000_cfn/000_official/000_AWS_EFS.json | 2 +- .../000_cfn/000_official/000_AWS_EKS.json | 2 +- .../000_cfn/000_official/000_AWS_EMR.json | 2 +- .../000_official/000_AWS_EMRContainers.json | 2 +- .../000_official/000_AWS_EMRServerless.json | 2 +- .../000_official/000_AWS_ElastiCache.json | 2 +- .../000_AWS_ElasticBeanstalk.json | 2 +- .../000_AWS_ElasticLoadBalancing.json | 2 +- .../000_AWS_ElasticLoadBalancingV2.json | 2 +- .../000_official/000_AWS_Elasticsearch.json | 2 +- .../000_official/000_AWS_EventSchemas.json | 2 +- .../000_cfn/000_official/000_AWS_Events.json | 2 +- .../000_official/000_AWS_Evidently.json | 2 +- .../000_cfn/000_official/000_AWS_FIS.json | 2 +- .../000_cfn/000_official/000_AWS_FMS.json | 2 +- .../000_cfn/000_official/000_AWS_FSx.json | 2 +- .../000_official/000_AWS_FinSpace.json | 2 +- .../000_official/000_AWS_Forecast.json | 2 +- .../000_official/000_AWS_FraudDetector.json | 2 +- .../000_official/000_AWS_GameLift.json | 2 +- .../000_AWS_GlobalAccelerator.json | 2 +- .../000_cfn/000_official/000_AWS_Glue.json | 2 +- .../000_official/000_AWS_Greengrass.json | 2 +- .../000_official/000_AWS_GreengrassV2.json | 2 +- .../000_official/000_AWS_GroundStation.json | 2 +- .../000_official/000_AWS_GuardDuty.json | 2 +- .../000_official/000_AWS_HealthLake.json | 2 +- .../000_cfn/000_official/000_AWS_IAM.json | 2 +- .../000_cfn/000_official/000_AWS_IVS.json | 2 +- .../000_official/000_AWS_ImageBuilder.json | 2 +- .../000_official/000_AWS_Inspector.json | 2 +- .../000_official/000_AWS_InspectorV2.json | 2 +- .../000_cfn/000_official/000_AWS_IoT.json | 8 +- .../000_official/000_AWS_IoT1Click.json | 2 +- .../000_official/000_AWS_IoTAnalytics.json | 2 +- .../000_AWS_IoTCoreDeviceAdvisor.json | 2 +- .../000_official/000_AWS_IoTEvents.json | 2 +- .../000_official/000_AWS_IoTFleetHub.json | 2 +- .../000_official/000_AWS_IoTSiteWise.json | 2 +- .../000_official/000_AWS_IoTThingsGraph.json | 2 +- .../000_official/000_AWS_IoTTwinMaker.json | 2 +- .../000_official/000_AWS_IoTWireless.json | 2 +- .../000_cfn/000_official/000_AWS_KMS.json | 2 +- .../000_official/000_AWS_KafkaConnect.json | 2 +- .../000_cfn/000_official/000_AWS_Kendra.json | 2 +- .../000_cfn/000_official/000_AWS_Kinesis.json | 2 +- .../000_AWS_KinesisAnalytics.json | 2 +- .../000_AWS_KinesisAnalyticsV2.json | 2 +- .../000_official/000_AWS_KinesisFirehose.json | 2 +- .../000_official/000_AWS_KinesisVideo.json | 2 +- .../000_official/000_AWS_LakeFormation.json | 2 +- .../000_cfn/000_official/000_AWS_Lambda.json | 2 +- .../000_cfn/000_official/000_AWS_Lex.json | 2 +- .../000_official/000_AWS_LicenseManager.json | 2 +- .../000_official/000_AWS_Lightsail.json | 2 +- .../000_official/000_AWS_Location.json | 2 +- .../000_cfn/000_official/000_AWS_Logs.json | 54 ++++++++++-- .../000_AWS_LookoutEquipment.json | 2 +- .../000_official/000_AWS_LookoutMetrics.json | 2 +- .../000_official/000_AWS_LookoutVision.json | 2 +- .../000_cfn/000_official/000_AWS_MSK.json | 2 +- .../000_cfn/000_official/000_AWS_MWAA.json | 2 +- .../000_cfn/000_official/000_AWS_Macie.json | 2 +- .../000_AWS_ManagedBlockchain.json | 2 +- .../000_official/000_AWS_MediaConnect.json | 2 +- .../000_official/000_AWS_MediaConvert.json | 2 +- .../000_official/000_AWS_MediaLive.json | 2 +- .../000_official/000_AWS_MediaPackage.json | 2 +- .../000_official/000_AWS_MediaStore.json | 2 +- .../000_official/000_AWS_MediaTailor.json | 2 +- .../000_official/000_AWS_MemoryDB.json | 2 +- .../000_cfn/000_official/000_AWS_Neptune.json | 2 +- .../000_official/000_AWS_NetworkFirewall.json | 2 +- .../000_official/000_AWS_NetworkManager.json | 2 +- .../000_official/000_AWS_NimbleStudio.json | 2 +- .../000_AWS_OpenSearchService.json | 2 +- .../000_official/000_AWS_OpsWorks.json | 2 +- .../000_official/000_AWS_OpsWorksCM.json | 2 +- .../000_official/000_AWS_Panorama.json | 2 +- .../000_official/000_AWS_Personalize.json | 2 +- .../000_official/000_AWS_Pinpoint.json | 2 +- .../000_official/000_AWS_PinpointEmail.json | 2 +- .../000_cfn/000_official/000_AWS_QLDB.json | 2 +- .../000_official/000_AWS_QuickSight.json | 2 +- .../000_cfn/000_official/000_AWS_RAM.json | 2 +- .../000_cfn/000_official/000_AWS_RDS.json | 2 +- .../000_cfn/000_official/000_AWS_RUM.json | 2 +- .../000_official/000_AWS_Redshift.json | 2 +- .../000_AWS_RedshiftServerless.json | 86 ++++++++++++++++++- .../000_official/000_AWS_RefactorSpaces.json | 2 +- .../000_official/000_AWS_ResilienceHub.json | 2 +- .../000_official/000_AWS_ResourceGroups.json | 2 +- .../000_official/000_AWS_RoboMaker.json | 2 +- .../000_cfn/000_official/000_AWS_Route53.json | 2 +- .../000_AWS_Route53RecoveryControl.json | 2 +- .../000_AWS_Route53RecoveryReadiness.json | 2 +- .../000_official/000_AWS_Route53Resolver.json | 2 +- .../000_cfn/000_official/000_AWS_S3.json | 2 +- .../000_official/000_AWS_S3ObjectLambda.json | 2 +- .../000_official/000_AWS_S3Outposts.json | 2 +- .../000_cfn/000_official/000_AWS_SDB.json | 2 +- .../000_cfn/000_official/000_AWS_SES.json | 2 +- .../000_cfn/000_official/000_AWS_SNS.json | 2 +- .../000_cfn/000_official/000_AWS_SQS.json | 2 +- .../000_cfn/000_official/000_AWS_SSM.json | 2 +- .../000_official/000_AWS_SSMContacts.json | 2 +- .../000_official/000_AWS_SSMIncidents.json | 10 ++- .../000_cfn/000_official/000_AWS_SSO.json | 2 +- .../000_official/000_AWS_SecretsManager.json | 2 +- .../000_official/000_AWS_SecurityHub.json | 2 +- .../000_official/000_AWS_ServiceCatalog.json | 2 +- .../000_AWS_ServiceCatalogAppRegistry.json | 2 +- .../000_AWS_ServiceDiscovery.json | 2 +- .../000_cfn/000_official/000_AWS_Signer.json | 2 +- .../000_official/000_AWS_StepFunctions.json | 4 +- .../000_official/000_AWS_Synthetics.json | 2 +- .../000_official/000_AWS_Timestream.json | 2 +- .../000_official/000_AWS_Transfer.json | 2 +- .../000_cfn/000_official/000_AWS_VoiceID.json | 2 +- .../000_cfn/000_official/000_AWS_WAF.json | 2 +- .../000_official/000_AWS_WAFRegional.json | 2 +- .../000_cfn/000_official/000_AWS_WAFv2.json | 2 +- .../000_cfn/000_official/000_AWS_Wisdom.json | 2 +- .../000_official/000_AWS_WorkSpaces.json | 2 +- .../000_cfn/000_official/000_AWS_XRay.json | 2 +- .../000_cfn/000_official/000_Alexa_ASK.json | 2 +- .../000_cfn/000_official/000_Tag.json | 2 +- .../000_cfn/000_official/001_Version.json | 2 +- 193 files changed, 395 insertions(+), 202 deletions(-) diff --git a/packages/@aws-cdk/cfnspec/CHANGELOG.md b/packages/@aws-cdk/cfnspec/CHANGELOG.md index 41a4a07343e52..50bb2ab389f94 100644 --- a/packages/@aws-cdk/cfnspec/CHANGELOG.md +++ b/packages/@aws-cdk/cfnspec/CHANGELOG.md @@ -1,3 +1,58 @@ +# CloudFormation Resource Specification v79.0.0 + +## New Resource Types + +* AWS::RedshiftServerless::Workgroup + +## Attribute Changes + + +## Property Changes + +* AWS::IoT::CACertificate CertificateMode (__added__) +* AWS::IoT::CACertificate CACertificatePem.Required (__changed__) + * Old: false + * New: true +* AWS::Logs::MetricFilter FilterName (__added__) +* AWS::Logs::MetricFilter FilterPattern.Documentation (__changed__) + * Old: http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-logs-metricfilter.html#cfn-cwl-metricfilter-filterpattern + * New: http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-logs-metricfilter.html#cfn-logs-metricfilter-filterpattern +* AWS::Logs::MetricFilter LogGroupName.Documentation (__changed__) + * Old: http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-logs-metricfilter.html#cfn-cwl-metricfilter-loggroupname + * New: http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-logs-metricfilter.html#cfn-logs-metricfilter-loggroupname +* AWS::Logs::MetricFilter MetricTransformations.DuplicatesAllowed (__deleted__) +* AWS::Logs::MetricFilter MetricTransformations.Documentation (__changed__) + * Old: http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-logs-metricfilter.html#cfn-cwl-metricfilter-metrictransformations + * New: http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-logs-metricfilter.html#cfn-logs-metricfilter-metrictransformations +* AWS::StepFunctions::StateMachine DefinitionSubstitutions.PrimitiveItemType (__changed__) + * Old: String + * New: Json + +## Property Type Changes + +* AWS::Logs::MetricFilter.Dimension (__added__) +* AWS::CloudFormation::StackSet.DeploymentTargets AccountFilterType (__added__) +* AWS::Logs::MetricFilter.MetricTransformation Dimensions (__added__) +* AWS::Logs::MetricFilter.MetricTransformation Unit (__added__) +* AWS::Logs::MetricFilter.MetricTransformation DefaultValue.Documentation (__changed__) + * Old: http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-logs-metricfilter-metrictransformation.html#cfn-cwl-metricfilter-metrictransformation-defaultvalue + * New: http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-logs-metricfilter-metrictransformation.html#cfn-logs-metricfilter-metrictransformation-defaultvalue +* AWS::Logs::MetricFilter.MetricTransformation MetricName.Documentation (__changed__) + * Old: http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-logs-metricfilter-metrictransformation.html#cfn-cwl-metricfilter-metrictransformation-metricname + * New: http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-logs-metricfilter-metrictransformation.html#cfn-logs-metricfilter-metrictransformation-metricname +* AWS::Logs::MetricFilter.MetricTransformation MetricNamespace.Documentation (__changed__) + * Old: http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-logs-metricfilter-metrictransformation.html#cfn-cwl-metricfilter-metrictransformation-metricnamespace + * New: http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-logs-metricfilter-metrictransformation.html#cfn-logs-metricfilter-metrictransformation-metricnamespace +* AWS::Logs::MetricFilter.MetricTransformation MetricValue.Documentation (__changed__) + * Old: http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-logs-metricfilter-metrictransformation.html#cfn-cwl-metricfilter-metrictransformation-metricvalue + * New: http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-logs-metricfilter-metrictransformation.html#cfn-logs-metricfilter-metrictransformation-metricvalue +* AWS::SSMIncidents::ResponsePlan.IncidentTemplate IncidentTags (__added__) + +## Unapplied changes + +* AWS::Rekognition is at 68.0.0 +* AWS::SageMaker is at 72.0.0 + # CloudFormation Resource Specification v78.1.0 ## New Resource Types diff --git a/packages/@aws-cdk/cfnspec/cfn.version b/packages/@aws-cdk/cfnspec/cfn.version index f26f41190ea02..971544f3d6e8a 100644 --- a/packages/@aws-cdk/cfnspec/cfn.version +++ b/packages/@aws-cdk/cfnspec/cfn.version @@ -1 +1 @@ -78.1.0 +79.0.0 diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_ACMPCA.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_ACMPCA.json index f990d628b63e0..71e3c406079e5 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_ACMPCA.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_ACMPCA.json @@ -1,5 +1,5 @@ { - "$version": "78.1.0", + "$version": "79.0.0", "PropertyTypes": { "AWS::ACMPCA::Certificate.ApiPassthrough": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-acmpca-certificate-apipassthrough.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_APS.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_APS.json index d225c4aeaefa1..27eeec0e9bab5 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_APS.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_APS.json @@ -1,5 +1,5 @@ { - "$version": "78.1.0", + "$version": "79.0.0", "PropertyTypes": {}, "ResourceTypes": { "AWS::APS::RuleGroupsNamespace": { diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_AccessAnalyzer.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_AccessAnalyzer.json index a42006ee9baec..4becdabcc23f1 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_AccessAnalyzer.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_AccessAnalyzer.json @@ -1,5 +1,5 @@ { - "$version": "78.1.0", + "$version": "79.0.0", "PropertyTypes": { "AWS::AccessAnalyzer::Analyzer.ArchiveRule": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-accessanalyzer-analyzer-archiverule.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_AmazonMQ.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_AmazonMQ.json index e854d6ff35703..5513d26de9f2c 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_AmazonMQ.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_AmazonMQ.json @@ -1,5 +1,5 @@ { - "$version": "78.1.0", + "$version": "79.0.0", "PropertyTypes": { "AWS::AmazonMQ::Broker.ConfigurationId": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-amazonmq-broker-configurationid.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Amplify.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Amplify.json index 927ffe985e9eb..c0763af3b5bec 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Amplify.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Amplify.json @@ -1,5 +1,5 @@ { - "$version": "78.1.0", + "$version": "79.0.0", "PropertyTypes": { "AWS::Amplify::App.AutoBranchCreationConfig": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-amplify-app-autobranchcreationconfig.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_AmplifyUIBuilder.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_AmplifyUIBuilder.json index 9aaa00f4e8285..4dbd02a6eb8c9 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_AmplifyUIBuilder.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_AmplifyUIBuilder.json @@ -1,5 +1,5 @@ { - "$version": "78.1.0", + "$version": "79.0.0", "PropertyTypes": { "AWS::AmplifyUIBuilder::Component.ActionParameters": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-amplifyuibuilder-component-actionparameters.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_ApiGateway.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_ApiGateway.json index 1f02cabe11a12..981d1ac8ead37 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_ApiGateway.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_ApiGateway.json @@ -1,5 +1,5 @@ { - "$version": "78.1.0", + "$version": "79.0.0", "PropertyTypes": { "AWS::ApiGateway::ApiKey.StageKey": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-apigateway-apikey-stagekey.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_ApiGatewayV2.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_ApiGatewayV2.json index 4fb44602b7ac4..bcdd85601ef3d 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_ApiGatewayV2.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_ApiGatewayV2.json @@ -1,5 +1,5 @@ { - "$version": "78.1.0", + "$version": "79.0.0", "PropertyTypes": { "AWS::ApiGatewayV2::Api.BodyS3Location": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-apigatewayv2-api-bodys3location.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_AppConfig.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_AppConfig.json index 77ee24393d37d..deff925e70d68 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_AppConfig.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_AppConfig.json @@ -1,5 +1,5 @@ { - "$version": "78.1.0", + "$version": "79.0.0", "PropertyTypes": { "AWS::AppConfig::Application.Tags": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-appconfig-application-tags.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_AppFlow.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_AppFlow.json index 130e4722fe89f..32a7c156ff5e6 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_AppFlow.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_AppFlow.json @@ -1,5 +1,5 @@ { - "$version": "78.1.0", + "$version": "79.0.0", "PropertyTypes": { "AWS::AppFlow::ConnectorProfile.AmplitudeConnectorProfileCredentials": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-appflow-connectorprofile-amplitudeconnectorprofilecredentials.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_AppIntegrations.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_AppIntegrations.json index 721fcb772d678..133ad426c9507 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_AppIntegrations.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_AppIntegrations.json @@ -1,5 +1,5 @@ { - "$version": "78.1.0", + "$version": "79.0.0", "PropertyTypes": { "AWS::AppIntegrations::DataIntegration.ScheduleConfig": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-appintegrations-dataintegration-scheduleconfig.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_AppMesh.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_AppMesh.json index 7bc079c57c2fe..1b4e2e7a9008b 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_AppMesh.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_AppMesh.json @@ -1,5 +1,5 @@ { - "$version": "78.1.0", + "$version": "79.0.0", "PropertyTypes": { "AWS::AppMesh::GatewayRoute.GatewayRouteHostnameMatch": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-appmesh-gatewayroute-gatewayroutehostnamematch.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_AppRunner.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_AppRunner.json index d4636bc175ee9..5cd652ad95b61 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_AppRunner.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_AppRunner.json @@ -1,5 +1,5 @@ { - "$version": "78.1.0", + "$version": "79.0.0", "PropertyTypes": { "AWS::AppRunner::ObservabilityConfiguration.TraceConfiguration": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-apprunner-observabilityconfiguration-traceconfiguration.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_AppStream.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_AppStream.json index 9b5d8260602a2..6b6d3477a4e55 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_AppStream.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_AppStream.json @@ -1,5 +1,5 @@ { - "$version": "78.1.0", + "$version": "79.0.0", "PropertyTypes": { "AWS::AppStream::AppBlock.S3Location": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-appstream-appblock-s3location.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_AppSync.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_AppSync.json index 0a0c808a336b7..2dd98c0f86fef 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_AppSync.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_AppSync.json @@ -1,5 +1,5 @@ { - "$version": "78.1.0", + "$version": "79.0.0", "PropertyTypes": { "AWS::AppSync::DataSource.AuthorizationConfig": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-appsync-datasource-authorizationconfig.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_ApplicationAutoScaling.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_ApplicationAutoScaling.json index cc461c2fe4fe7..e4d8d34cfc87a 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_ApplicationAutoScaling.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_ApplicationAutoScaling.json @@ -1,5 +1,5 @@ { - "$version": "78.1.0", + "$version": "79.0.0", "PropertyTypes": { "AWS::ApplicationAutoScaling::ScalableTarget.ScalableTargetAction": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-applicationautoscaling-scalabletarget-scalabletargetaction.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_ApplicationInsights.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_ApplicationInsights.json index 2096043b580ed..68a940afc69e5 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_ApplicationInsights.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_ApplicationInsights.json @@ -1,5 +1,5 @@ { - "$version": "78.1.0", + "$version": "79.0.0", "PropertyTypes": { "AWS::ApplicationInsights::Application.Alarm": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-applicationinsights-application-alarm.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Athena.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Athena.json index 5305effee7610..a2233e18b024b 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Athena.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Athena.json @@ -1,5 +1,5 @@ { - "$version": "78.1.0", + "$version": "79.0.0", "PropertyTypes": { "AWS::Athena::WorkGroup.EncryptionConfiguration": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-athena-workgroup-encryptionconfiguration.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_AuditManager.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_AuditManager.json index 35f07a49e8c94..c03ec145d8fc3 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_AuditManager.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_AuditManager.json @@ -1,5 +1,5 @@ { - "$version": "78.1.0", + "$version": "79.0.0", "PropertyTypes": { "AWS::AuditManager::Assessment.AWSAccount": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-auditmanager-assessment-awsaccount.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_AutoScaling.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_AutoScaling.json index b4671ab17ecb0..554982efcf9b1 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_AutoScaling.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_AutoScaling.json @@ -1,5 +1,5 @@ { - "$version": "78.1.0", + "$version": "79.0.0", "PropertyTypes": { "AWS::AutoScaling::AutoScalingGroup.AcceleratorCountRequest": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-autoscaling-autoscalinggroup-acceleratorcountrequest.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_AutoScalingPlans.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_AutoScalingPlans.json index 743408d830bd3..00e11d82e609e 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_AutoScalingPlans.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_AutoScalingPlans.json @@ -1,5 +1,5 @@ { - "$version": "78.1.0", + "$version": "79.0.0", "PropertyTypes": { "AWS::AutoScalingPlans::ScalingPlan.ApplicationSource": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-autoscalingplans-scalingplan-applicationsource.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Backup.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Backup.json index 43882a76c3a79..b46c290deb5c5 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Backup.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Backup.json @@ -1,5 +1,5 @@ { - "$version": "78.1.0", + "$version": "79.0.0", "PropertyTypes": { "AWS::Backup::BackupPlan.AdvancedBackupSettingResourceType": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-backup-backupplan-advancedbackupsettingresourcetype.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Batch.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Batch.json index 9848a3afed7ce..0972eb18c2591 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Batch.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Batch.json @@ -1,5 +1,5 @@ { - "$version": "78.1.0", + "$version": "79.0.0", "PropertyTypes": { "AWS::Batch::ComputeEnvironment.ComputeResources": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-batch-computeenvironment-computeresources.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_BillingConductor.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_BillingConductor.json index a0a6ea8f10b00..16700eacdf4a5 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_BillingConductor.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_BillingConductor.json @@ -1,5 +1,5 @@ { - "$version": "78.1.0", + "$version": "79.0.0", "PropertyTypes": { "AWS::BillingConductor::BillingGroup.AccountGrouping": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-billingconductor-billinggroup-accountgrouping.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Budgets.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Budgets.json index ea896564cb635..46f2d6a806fd1 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Budgets.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Budgets.json @@ -1,5 +1,5 @@ { - "$version": "78.1.0", + "$version": "79.0.0", "PropertyTypes": { "AWS::Budgets::Budget.BudgetData": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-budgets-budget-budgetdata.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_CE.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_CE.json index 214e32b061aae..b1d9e8cef460e 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_CE.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_CE.json @@ -1,5 +1,5 @@ { - "$version": "78.1.0", + "$version": "79.0.0", "PropertyTypes": { "AWS::CE::AnomalyMonitor.ResourceTag": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-ce-anomalymonitor-resourcetag.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_CUR.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_CUR.json index 0993c2901f921..55bf7a27bf010 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_CUR.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_CUR.json @@ -1,5 +1,5 @@ { - "$version": "78.1.0", + "$version": "79.0.0", "PropertyTypes": {}, "ResourceTypes": { "AWS::CUR::ReportDefinition": { diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Cassandra.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Cassandra.json index 3e3dc5db123cd..b70d71ad15d13 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Cassandra.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Cassandra.json @@ -1,5 +1,5 @@ { - "$version": "78.1.0", + "$version": "79.0.0", "PropertyTypes": { "AWS::Cassandra::Table.BillingMode": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-cassandra-table-billingmode.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_CertificateManager.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_CertificateManager.json index 3455e490dbd57..6706e45023fef 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_CertificateManager.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_CertificateManager.json @@ -1,5 +1,5 @@ { - "$version": "78.1.0", + "$version": "79.0.0", "PropertyTypes": { "AWS::CertificateManager::Account.ExpiryEventsConfiguration": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-certificatemanager-account-expiryeventsconfiguration.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Chatbot.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Chatbot.json index 4c7b5a77fb78b..6fa167ca75e1f 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Chatbot.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Chatbot.json @@ -1,5 +1,5 @@ { - "$version": "78.1.0", + "$version": "79.0.0", "PropertyTypes": {}, "ResourceTypes": { "AWS::Chatbot::SlackChannelConfiguration": { diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Cloud9.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Cloud9.json index 21ee8e796da91..3d3be2a078abe 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Cloud9.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Cloud9.json @@ -1,5 +1,5 @@ { - "$version": "78.1.0", + "$version": "79.0.0", "PropertyTypes": { "AWS::Cloud9::EnvironmentEC2.Repository": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-cloud9-environmentec2-repository.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_CloudFormation.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_CloudFormation.json index a94ae6903ce2a..9ac442e40ebf4 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_CloudFormation.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_CloudFormation.json @@ -1,5 +1,5 @@ { - "$version": "78.1.0", + "$version": "79.0.0", "PropertyTypes": { "AWS::CloudFormation::HookVersion.LoggingConfig": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-cloudformation-hookversion-loggingconfig.html", @@ -55,6 +55,12 @@ "AWS::CloudFormation::StackSet.DeploymentTargets": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-cloudformation-stackset-deploymenttargets.html", "Properties": { + "AccountFilterType": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-cloudformation-stackset-deploymenttargets.html#cfn-cloudformation-stackset-deploymenttargets-accountfiltertype", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + }, "Accounts": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-cloudformation-stackset-deploymenttargets.html#cfn-cloudformation-stackset-deploymenttargets-accounts", "DuplicatesAllowed": false, diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_CloudFront.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_CloudFront.json index 1bf2f6dac97cc..de1375ba1e323 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_CloudFront.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_CloudFront.json @@ -1,5 +1,5 @@ { - "$version": "78.1.0", + "$version": "79.0.0", "PropertyTypes": { "AWS::CloudFront::CachePolicy.CachePolicyConfig": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-cloudfront-cachepolicy-cachepolicyconfig.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_CloudTrail.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_CloudTrail.json index 65906d2cdb748..f69945bba040f 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_CloudTrail.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_CloudTrail.json @@ -1,5 +1,5 @@ { - "$version": "78.1.0", + "$version": "79.0.0", "PropertyTypes": { "AWS::CloudTrail::EventDataStore.AdvancedEventSelector": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-cloudtrail-eventdatastore-advancedeventselector.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_CloudWatch.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_CloudWatch.json index 85d04d11c2e3e..a44f1d7dda962 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_CloudWatch.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_CloudWatch.json @@ -1,5 +1,5 @@ { - "$version": "78.1.0", + "$version": "79.0.0", "PropertyTypes": { "AWS::CloudWatch::Alarm.Dimension": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-cw-dimension.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_CodeArtifact.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_CodeArtifact.json index 3520a46780d96..31702eace9c29 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_CodeArtifact.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_CodeArtifact.json @@ -1,5 +1,5 @@ { - "$version": "78.1.0", + "$version": "79.0.0", "PropertyTypes": {}, "ResourceTypes": { "AWS::CodeArtifact::Domain": { diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_CodeBuild.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_CodeBuild.json index a24e4548cbe88..c16d05a8c1557 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_CodeBuild.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_CodeBuild.json @@ -1,5 +1,5 @@ { - "$version": "78.1.0", + "$version": "79.0.0", "PropertyTypes": { "AWS::CodeBuild::Project.Artifacts": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-codebuild-project-artifacts.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_CodeCommit.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_CodeCommit.json index 018cf3b32f292..87e928b8f2ec9 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_CodeCommit.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_CodeCommit.json @@ -1,5 +1,5 @@ { - "$version": "78.1.0", + "$version": "79.0.0", "PropertyTypes": { "AWS::CodeCommit::Repository.Code": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-codecommit-repository-code.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_CodeDeploy.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_CodeDeploy.json index 99a075fd518b6..d0b4170239536 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_CodeDeploy.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_CodeDeploy.json @@ -1,5 +1,5 @@ { - "$version": "78.1.0", + "$version": "79.0.0", "PropertyTypes": { "AWS::CodeDeploy::DeploymentConfig.MinimumHealthyHosts": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-codedeploy-deploymentconfig-minimumhealthyhosts.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_CodeGuruProfiler.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_CodeGuruProfiler.json index eb95353bf78fc..7a4a1f9f4d3cf 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_CodeGuruProfiler.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_CodeGuruProfiler.json @@ -1,5 +1,5 @@ { - "$version": "78.1.0", + "$version": "79.0.0", "PropertyTypes": { "AWS::CodeGuruProfiler::ProfilingGroup.Channel": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-codeguruprofiler-profilinggroup-channel.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_CodeGuruReviewer.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_CodeGuruReviewer.json index 510ec909dbf3c..a458420e560bd 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_CodeGuruReviewer.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_CodeGuruReviewer.json @@ -1,5 +1,5 @@ { - "$version": "78.1.0", + "$version": "79.0.0", "PropertyTypes": {}, "ResourceTypes": { "AWS::CodeGuruReviewer::RepositoryAssociation": { diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_CodePipeline.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_CodePipeline.json index e85c98be9efc2..7c1e87a78e074 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_CodePipeline.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_CodePipeline.json @@ -1,5 +1,5 @@ { - "$version": "78.1.0", + "$version": "79.0.0", "PropertyTypes": { "AWS::CodePipeline::CustomActionType.ArtifactDetails": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-codepipeline-customactiontype-artifactdetails.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_CodeStar.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_CodeStar.json index 5faa9d13681d3..b49caf629135d 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_CodeStar.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_CodeStar.json @@ -1,5 +1,5 @@ { - "$version": "78.1.0", + "$version": "79.0.0", "PropertyTypes": { "AWS::CodeStar::GitHubRepository.Code": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-codestar-githubrepository-code.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_CodeStarConnections.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_CodeStarConnections.json index 5a6f61fc8c21e..789bb076b3f0d 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_CodeStarConnections.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_CodeStarConnections.json @@ -1,5 +1,5 @@ { - "$version": "78.1.0", + "$version": "79.0.0", "PropertyTypes": {}, "ResourceTypes": { "AWS::CodeStarConnections::Connection": { diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_CodeStarNotifications.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_CodeStarNotifications.json index 4f8f3c68e9895..c3db0b0afd9a9 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_CodeStarNotifications.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_CodeStarNotifications.json @@ -1,5 +1,5 @@ { - "$version": "78.1.0", + "$version": "79.0.0", "PropertyTypes": { "AWS::CodeStarNotifications::NotificationRule.Target": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-codestarnotifications-notificationrule-target.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Cognito.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Cognito.json index 66192c9b27757..68f1d2cdf8055 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Cognito.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Cognito.json @@ -1,5 +1,5 @@ { - "$version": "78.1.0", + "$version": "79.0.0", "PropertyTypes": { "AWS::Cognito::IdentityPool.CognitoIdentityProvider": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-cognito-identitypool-cognitoidentityprovider.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Config.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Config.json index 3bfae34a4acf8..8550e4c188fe0 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Config.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Config.json @@ -1,5 +1,5 @@ { - "$version": "78.1.0", + "$version": "79.0.0", "PropertyTypes": { "AWS::Config::ConfigRule.Scope": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-config-configrule-scope.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Connect.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Connect.json index 3927b3a887b34..807d9c2eb4648 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Connect.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Connect.json @@ -1,5 +1,5 @@ { - "$version": "78.1.0", + "$version": "79.0.0", "PropertyTypes": { "AWS::Connect::HoursOfOperation.HoursOfOperationConfig": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-connect-hoursofoperation-hoursofoperationconfig.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_CustomerProfiles.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_CustomerProfiles.json index d88a546b31879..dbc16ed4e10de 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_CustomerProfiles.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_CustomerProfiles.json @@ -1,5 +1,5 @@ { - "$version": "78.1.0", + "$version": "79.0.0", "PropertyTypes": { "AWS::CustomerProfiles::Integration.ConnectorOperator": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-customerprofiles-integration-connectoroperator.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_DAX.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_DAX.json index 40d5379ab0424..8f1450b4239ca 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_DAX.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_DAX.json @@ -1,5 +1,5 @@ { - "$version": "78.1.0", + "$version": "79.0.0", "PropertyTypes": { "AWS::DAX::Cluster.SSESpecification": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-dax-cluster-ssespecification.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_DLM.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_DLM.json index c811952b44b06..6157077d1814b 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_DLM.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_DLM.json @@ -1,5 +1,5 @@ { - "$version": "78.1.0", + "$version": "79.0.0", "PropertyTypes": { "AWS::DLM::LifecyclePolicy.Action": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-dlm-lifecyclepolicy-action.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_DMS.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_DMS.json index 8fd485a2b8d2b..b688984baabeb 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_DMS.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_DMS.json @@ -1,5 +1,5 @@ { - "$version": "78.1.0", + "$version": "79.0.0", "PropertyTypes": { "AWS::DMS::Endpoint.DocDbSettings": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-dms-endpoint-docdbsettings.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_DataBrew.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_DataBrew.json index f71227eefeb38..00895186ed2f5 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_DataBrew.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_DataBrew.json @@ -1,5 +1,5 @@ { - "$version": "78.1.0", + "$version": "79.0.0", "PropertyTypes": { "AWS::DataBrew::Dataset.CsvOptions": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-databrew-dataset-csvoptions.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_DataPipeline.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_DataPipeline.json index dfcd84b4f5f00..d50c25285fd23 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_DataPipeline.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_DataPipeline.json @@ -1,5 +1,5 @@ { - "$version": "78.1.0", + "$version": "79.0.0", "PropertyTypes": { "AWS::DataPipeline::Pipeline.Field": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-datapipeline-pipeline-pipelineobjects-fields.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_DataSync.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_DataSync.json index c72f8f7c809b5..058dd7fb8919c 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_DataSync.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_DataSync.json @@ -1,5 +1,5 @@ { - "$version": "78.1.0", + "$version": "79.0.0", "PropertyTypes": { "AWS::DataSync::LocationEFS.Ec2Config": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-datasync-locationefs-ec2config.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Detective.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Detective.json index 45ac186de2faa..3cfdf48597cde 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Detective.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Detective.json @@ -1,5 +1,5 @@ { - "$version": "78.1.0", + "$version": "79.0.0", "PropertyTypes": {}, "ResourceTypes": { "AWS::Detective::Graph": { diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_DevOpsGuru.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_DevOpsGuru.json index 615e80dc443bb..29288450342c6 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_DevOpsGuru.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_DevOpsGuru.json @@ -1,5 +1,5 @@ { - "$version": "78.1.0", + "$version": "79.0.0", "PropertyTypes": { "AWS::DevOpsGuru::NotificationChannel.NotificationChannelConfig": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-devopsguru-notificationchannel-notificationchannelconfig.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_DirectoryService.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_DirectoryService.json index 481e35a0a170d..6c935899fd655 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_DirectoryService.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_DirectoryService.json @@ -1,5 +1,5 @@ { - "$version": "78.1.0", + "$version": "79.0.0", "PropertyTypes": { "AWS::DirectoryService::MicrosoftAD.VpcSettings": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-directoryservice-microsoftad-vpcsettings.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_DocDB.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_DocDB.json index 9eb795ff91a7c..2a20933479b34 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_DocDB.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_DocDB.json @@ -1,5 +1,5 @@ { - "$version": "78.1.0", + "$version": "79.0.0", "PropertyTypes": {}, "ResourceTypes": { "AWS::DocDB::DBCluster": { diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_DynamoDB.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_DynamoDB.json index 2289c4d97afa0..ae55d8b2b6f9b 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_DynamoDB.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_DynamoDB.json @@ -1,5 +1,5 @@ { - "$version": "78.1.0", + "$version": "79.0.0", "PropertyTypes": { "AWS::DynamoDB::GlobalTable.AttributeDefinition": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-dynamodb-globaltable-attributedefinition.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_EC2.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_EC2.json index 32b78bfb13db4..05a9482b85aa7 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_EC2.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_EC2.json @@ -1,5 +1,5 @@ { - "$version": "78.1.0", + "$version": "79.0.0", "PropertyTypes": { "AWS::EC2::CapacityReservation.TagSpecification": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-ec2-capacityreservation-tagspecification.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_ECR.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_ECR.json index 556346062ec93..e2ec9c091ac89 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_ECR.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_ECR.json @@ -1,5 +1,5 @@ { - "$version": "78.1.0", + "$version": "79.0.0", "PropertyTypes": { "AWS::ECR::ReplicationConfiguration.ReplicationConfiguration": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-ecr-replicationconfiguration-replicationconfiguration.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_ECS.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_ECS.json index 031e5fe032ed4..6b1e11788e9fb 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_ECS.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_ECS.json @@ -1,5 +1,5 @@ { - "$version": "78.1.0", + "$version": "79.0.0", "PropertyTypes": { "AWS::ECS::CapacityProvider.AutoScalingGroupProvider": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-ecs-capacityprovider-autoscalinggroupprovider.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_EFS.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_EFS.json index 8ff2c312a3c18..5dff566e76a52 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_EFS.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_EFS.json @@ -1,5 +1,5 @@ { - "$version": "78.1.0", + "$version": "79.0.0", "PropertyTypes": { "AWS::EFS::AccessPoint.AccessPointTag": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-efs-accesspoint-accesspointtag.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_EKS.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_EKS.json index deb9d0a318d92..9715dd7fd3249 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_EKS.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_EKS.json @@ -1,5 +1,5 @@ { - "$version": "78.1.0", + "$version": "79.0.0", "PropertyTypes": { "AWS::EKS::Cluster.ClusterLogging": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-eks-cluster-clusterlogging.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_EMR.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_EMR.json index 661efc07a53a8..7a474f2b8935c 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_EMR.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_EMR.json @@ -1,5 +1,5 @@ { - "$version": "78.1.0", + "$version": "79.0.0", "PropertyTypes": { "AWS::EMR::Cluster.Application": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-elasticmapreduce-cluster-application.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_EMRContainers.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_EMRContainers.json index d1a6db0a6adcd..d2da4dec3b7f4 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_EMRContainers.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_EMRContainers.json @@ -1,5 +1,5 @@ { - "$version": "78.1.0", + "$version": "79.0.0", "PropertyTypes": { "AWS::EMRContainers::VirtualCluster.ContainerInfo": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-emrcontainers-virtualcluster-containerinfo.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_EMRServerless.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_EMRServerless.json index 3dfe10f41da05..bb21753a7af93 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_EMRServerless.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_EMRServerless.json @@ -1,5 +1,5 @@ { - "$version": "78.1.0", + "$version": "79.0.0", "PropertyTypes": { "AWS::EMRServerless::Application.AutoStartConfiguration": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-emrserverless-application-autostartconfiguration.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_ElastiCache.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_ElastiCache.json index a875ee9a83162..6640261e19315 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_ElastiCache.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_ElastiCache.json @@ -1,5 +1,5 @@ { - "$version": "78.1.0", + "$version": "79.0.0", "PropertyTypes": { "AWS::ElastiCache::CacheCluster.CloudWatchLogsDestinationDetails": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-elasticache-cachecluster-cloudwatchlogsdestinationdetails.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_ElasticBeanstalk.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_ElasticBeanstalk.json index ac3f124711257..fe9398005a8ba 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_ElasticBeanstalk.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_ElasticBeanstalk.json @@ -1,5 +1,5 @@ { - "$version": "78.1.0", + "$version": "79.0.0", "PropertyTypes": { "AWS::ElasticBeanstalk::Application.ApplicationResourceLifecycleConfig": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-elasticbeanstalk-application-applicationresourcelifecycleconfig.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_ElasticLoadBalancing.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_ElasticLoadBalancing.json index 3bfefa62f268e..ade5a8cf50c13 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_ElasticLoadBalancing.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_ElasticLoadBalancing.json @@ -1,5 +1,5 @@ { - "$version": "78.1.0", + "$version": "79.0.0", "PropertyTypes": { "AWS::ElasticLoadBalancing::LoadBalancer.AccessLoggingPolicy": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-ec2-elb-accessloggingpolicy.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_ElasticLoadBalancingV2.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_ElasticLoadBalancingV2.json index 05cf0ca3643b0..315522214bde5 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_ElasticLoadBalancingV2.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_ElasticLoadBalancingV2.json @@ -1,5 +1,5 @@ { - "$version": "78.1.0", + "$version": "79.0.0", "PropertyTypes": { "AWS::ElasticLoadBalancingV2::Listener.Action": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-elasticloadbalancingv2-listener-action.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Elasticsearch.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Elasticsearch.json index 8075c9099d4a0..2b4bba9742b73 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Elasticsearch.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Elasticsearch.json @@ -1,5 +1,5 @@ { - "$version": "78.1.0", + "$version": "79.0.0", "PropertyTypes": { "AWS::Elasticsearch::Domain.AdvancedSecurityOptionsInput": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-elasticsearch-domain-advancedsecurityoptionsinput.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_EventSchemas.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_EventSchemas.json index b41f9386e43c8..5e91cb1a3f096 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_EventSchemas.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_EventSchemas.json @@ -1,5 +1,5 @@ { - "$version": "78.1.0", + "$version": "79.0.0", "PropertyTypes": { "AWS::EventSchemas::Discoverer.TagsEntry": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-eventschemas-discoverer-tagsentry.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Events.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Events.json index 5686d54ccdc1a..731ce7007013a 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Events.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Events.json @@ -1,5 +1,5 @@ { - "$version": "78.1.0", + "$version": "79.0.0", "PropertyTypes": { "AWS::Events::Connection.ApiKeyAuthParameters": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-events-connection-apikeyauthparameters.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Evidently.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Evidently.json index f92f0c5d5bc6f..e8c9b7713f9c9 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Evidently.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Evidently.json @@ -1,5 +1,5 @@ { - "$version": "78.1.0", + "$version": "79.0.0", "PropertyTypes": { "AWS::Evidently::Experiment.MetricGoalObject": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-evidently-experiment-metricgoalobject.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_FIS.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_FIS.json index 0b8ff2314986b..249586ccba077 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_FIS.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_FIS.json @@ -1,5 +1,5 @@ { - "$version": "78.1.0", + "$version": "79.0.0", "PropertyTypes": { "AWS::FIS::ExperimentTemplate.ExperimentTemplateAction": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-fis-experimenttemplate-experimenttemplateaction.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_FMS.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_FMS.json index a1bb85e73e8d6..ba2e07f48900f 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_FMS.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_FMS.json @@ -1,5 +1,5 @@ { - "$version": "78.1.0", + "$version": "79.0.0", "PropertyTypes": { "AWS::FMS::Policy.IEMap": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-fms-policy-iemap.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_FSx.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_FSx.json index 6df50693aa2d0..98ffbeb28f825 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_FSx.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_FSx.json @@ -1,5 +1,5 @@ { - "$version": "78.1.0", + "$version": "79.0.0", "PropertyTypes": { "AWS::FSx::FileSystem.AuditLogConfiguration": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-fsx-filesystem-windowsconfiguration-auditlogconfiguration.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_FinSpace.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_FinSpace.json index c951e882500a8..7e274f7513051 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_FinSpace.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_FinSpace.json @@ -1,5 +1,5 @@ { - "$version": "78.1.0", + "$version": "79.0.0", "PropertyTypes": { "AWS::FinSpace::Environment.FederationParameters": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-finspace-environment-federationparameters.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Forecast.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Forecast.json index adc15088bef6a..99cfe6aa540e0 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Forecast.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Forecast.json @@ -1,5 +1,5 @@ { - "$version": "78.1.0", + "$version": "79.0.0", "PropertyTypes": {}, "ResourceTypes": { "AWS::Forecast::Dataset": { diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_FraudDetector.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_FraudDetector.json index 002204a8ff534..14135191d9f1b 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_FraudDetector.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_FraudDetector.json @@ -1,5 +1,5 @@ { - "$version": "78.1.0", + "$version": "79.0.0", "PropertyTypes": { "AWS::FraudDetector::Detector.EntityType": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-frauddetector-detector-entitytype.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_GameLift.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_GameLift.json index 8483a9da79dc5..156ba17b0af5a 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_GameLift.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_GameLift.json @@ -1,5 +1,5 @@ { - "$version": "78.1.0", + "$version": "79.0.0", "PropertyTypes": { "AWS::GameLift::Alias.RoutingStrategy": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-gamelift-alias-routingstrategy.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_GlobalAccelerator.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_GlobalAccelerator.json index d808bcceb754c..6a972675abdf4 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_GlobalAccelerator.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_GlobalAccelerator.json @@ -1,5 +1,5 @@ { - "$version": "78.1.0", + "$version": "79.0.0", "PropertyTypes": { "AWS::GlobalAccelerator::EndpointGroup.EndpointConfiguration": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-globalaccelerator-endpointgroup-endpointconfiguration.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Glue.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Glue.json index b72b6471b5d15..0b27c5c941295 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Glue.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Glue.json @@ -1,5 +1,5 @@ { - "$version": "78.1.0", + "$version": "79.0.0", "PropertyTypes": { "AWS::Glue::Classifier.CsvClassifier": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-glue-classifier-csvclassifier.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Greengrass.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Greengrass.json index 3249876d8a6cb..de1e182175c30 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Greengrass.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Greengrass.json @@ -1,5 +1,5 @@ { - "$version": "78.1.0", + "$version": "79.0.0", "PropertyTypes": { "AWS::Greengrass::ConnectorDefinition.Connector": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-greengrass-connectordefinition-connector.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_GreengrassV2.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_GreengrassV2.json index 795b21248413c..feb9474a82fab 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_GreengrassV2.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_GreengrassV2.json @@ -1,5 +1,5 @@ { - "$version": "78.1.0", + "$version": "79.0.0", "PropertyTypes": { "AWS::GreengrassV2::ComponentVersion.ComponentDependencyRequirement": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-greengrassv2-componentversion-componentdependencyrequirement.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_GroundStation.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_GroundStation.json index 8d90af4d732b8..e371a6f4b1c77 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_GroundStation.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_GroundStation.json @@ -1,5 +1,5 @@ { - "$version": "78.1.0", + "$version": "79.0.0", "PropertyTypes": { "AWS::GroundStation::Config.AntennaDownlinkConfig": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-groundstation-config-antennadownlinkconfig.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_GuardDuty.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_GuardDuty.json index f907bd1e3b816..8eb2c91c2e9b6 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_GuardDuty.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_GuardDuty.json @@ -1,5 +1,5 @@ { - "$version": "78.1.0", + "$version": "79.0.0", "PropertyTypes": { "AWS::GuardDuty::Detector.CFNDataSourceConfigurations": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-guardduty-detector-cfndatasourceconfigurations.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_HealthLake.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_HealthLake.json index afb8f5b1cb7ad..8e08ba880dd37 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_HealthLake.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_HealthLake.json @@ -1,5 +1,5 @@ { - "$version": "78.1.0", + "$version": "79.0.0", "PropertyTypes": { "AWS::HealthLake::FHIRDatastore.KmsEncryptionConfig": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-healthlake-fhirdatastore-kmsencryptionconfig.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_IAM.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_IAM.json index 7f0d1836bf7fd..cb241fe626ee3 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_IAM.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_IAM.json @@ -1,5 +1,5 @@ { - "$version": "78.1.0", + "$version": "79.0.0", "PropertyTypes": { "AWS::IAM::Group.Policy": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-iam-policy.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_IVS.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_IVS.json index 53eba94c5feaa..761dd0be104e6 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_IVS.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_IVS.json @@ -1,5 +1,5 @@ { - "$version": "78.1.0", + "$version": "79.0.0", "PropertyTypes": { "AWS::IVS::RecordingConfiguration.DestinationConfiguration": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-ivs-recordingconfiguration-destinationconfiguration.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_ImageBuilder.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_ImageBuilder.json index eec03535c7c7a..0607001c0c7b5 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_ImageBuilder.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_ImageBuilder.json @@ -1,5 +1,5 @@ { - "$version": "78.1.0", + "$version": "79.0.0", "PropertyTypes": { "AWS::ImageBuilder::ContainerRecipe.ComponentConfiguration": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-imagebuilder-containerrecipe-componentconfiguration.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Inspector.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Inspector.json index 08c0c8147ce45..2f4738b68aed4 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Inspector.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Inspector.json @@ -1,5 +1,5 @@ { - "$version": "78.1.0", + "$version": "79.0.0", "PropertyTypes": {}, "ResourceTypes": { "AWS::Inspector::AssessmentTarget": { diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_InspectorV2.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_InspectorV2.json index edc1fa6cf0d41..455e26b04f826 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_InspectorV2.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_InspectorV2.json @@ -1,5 +1,5 @@ { - "$version": "78.1.0", + "$version": "79.0.0", "PropertyTypes": { "AWS::InspectorV2::Filter.DateFilter": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-inspectorv2-filter-datefilter.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_IoT.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_IoT.json index 6a1260859cbb1..084b3b0951b4d 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_IoT.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_IoT.json @@ -1,5 +1,5 @@ { - "$version": "78.1.0", + "$version": "79.0.0", "PropertyTypes": { "AWS::IoT::AccountAuditConfiguration.AuditCheckConfiguration": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-iot-accountauditconfiguration-auditcheckconfiguration.html", @@ -1675,6 +1675,12 @@ "CACertificatePem": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-iot-cacertificate.html#cfn-iot-cacertificate-cacertificatepem", "PrimitiveType": "String", + "Required": true, + "UpdateType": "Immutable" + }, + "CertificateMode": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-iot-cacertificate.html#cfn-iot-cacertificate-certificatemode", + "PrimitiveType": "String", "Required": false, "UpdateType": "Immutable" }, diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_IoT1Click.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_IoT1Click.json index f3e7d4aa1b27e..0eb96375d11a9 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_IoT1Click.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_IoT1Click.json @@ -1,5 +1,5 @@ { - "$version": "78.1.0", + "$version": "79.0.0", "PropertyTypes": { "AWS::IoT1Click::Project.DeviceTemplate": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-iot1click-project-devicetemplate.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_IoTAnalytics.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_IoTAnalytics.json index 6d655aaf2539f..7df231f3760fe 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_IoTAnalytics.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_IoTAnalytics.json @@ -1,5 +1,5 @@ { - "$version": "78.1.0", + "$version": "79.0.0", "PropertyTypes": { "AWS::IoTAnalytics::Channel.ChannelStorage": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-iotanalytics-channel-channelstorage.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_IoTCoreDeviceAdvisor.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_IoTCoreDeviceAdvisor.json index a3519e3baf69b..a94c339952be7 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_IoTCoreDeviceAdvisor.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_IoTCoreDeviceAdvisor.json @@ -1,5 +1,5 @@ { - "$version": "78.1.0", + "$version": "79.0.0", "PropertyTypes": {}, "ResourceTypes": { "AWS::IoTCoreDeviceAdvisor::SuiteDefinition": { diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_IoTEvents.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_IoTEvents.json index b2cfbc7e7d8d1..80e8557136d0e 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_IoTEvents.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_IoTEvents.json @@ -1,5 +1,5 @@ { - "$version": "78.1.0", + "$version": "79.0.0", "PropertyTypes": { "AWS::IoTEvents::AlarmModel.AcknowledgeFlow": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-iotevents-alarmmodel-acknowledgeflow.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_IoTFleetHub.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_IoTFleetHub.json index 15a7376e17b65..935bc2b76c1a1 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_IoTFleetHub.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_IoTFleetHub.json @@ -1,5 +1,5 @@ { - "$version": "78.1.0", + "$version": "79.0.0", "PropertyTypes": {}, "ResourceTypes": { "AWS::IoTFleetHub::Application": { diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_IoTSiteWise.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_IoTSiteWise.json index f73b660b617ea..ecc4e730989b2 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_IoTSiteWise.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_IoTSiteWise.json @@ -1,5 +1,5 @@ { - "$version": "78.1.0", + "$version": "79.0.0", "PropertyTypes": { "AWS::IoTSiteWise::AccessPolicy.AccessPolicyIdentity": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-iotsitewise-accesspolicy-accesspolicyidentity.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_IoTThingsGraph.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_IoTThingsGraph.json index 0feaa40fe8ab9..82be485c8ae3b 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_IoTThingsGraph.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_IoTThingsGraph.json @@ -1,5 +1,5 @@ { - "$version": "78.1.0", + "$version": "79.0.0", "PropertyTypes": { "AWS::IoTThingsGraph::FlowTemplate.DefinitionDocument": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-iotthingsgraph-flowtemplate-definitiondocument.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_IoTTwinMaker.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_IoTTwinMaker.json index 38114cfd6013f..afcd02e10dde8 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_IoTTwinMaker.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_IoTTwinMaker.json @@ -1,5 +1,5 @@ { - "$version": "78.1.0", + "$version": "79.0.0", "PropertyTypes": { "AWS::IoTTwinMaker::ComponentType.DataConnector": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-iottwinmaker-componenttype-dataconnector.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_IoTWireless.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_IoTWireless.json index e68ab594d36ad..fe71ec6921618 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_IoTWireless.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_IoTWireless.json @@ -1,5 +1,5 @@ { - "$version": "78.1.0", + "$version": "79.0.0", "PropertyTypes": { "AWS::IoTWireless::DeviceProfile.LoRaWANDeviceProfile": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-iotwireless-deviceprofile-lorawandeviceprofile.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_KMS.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_KMS.json index 08fe2369bb69f..213b6f81d9416 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_KMS.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_KMS.json @@ -1,5 +1,5 @@ { - "$version": "78.1.0", + "$version": "79.0.0", "PropertyTypes": {}, "ResourceTypes": { "AWS::KMS::Alias": { diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_KafkaConnect.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_KafkaConnect.json index af80292d25cd0..996c9afbf0642 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_KafkaConnect.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_KafkaConnect.json @@ -1,5 +1,5 @@ { - "$version": "78.1.0", + "$version": "79.0.0", "PropertyTypes": { "AWS::KafkaConnect::Connector.ApacheKafkaCluster": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-kafkaconnect-connector-apachekafkacluster.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Kendra.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Kendra.json index 556535330016e..3f77a7a29109a 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Kendra.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Kendra.json @@ -1,5 +1,5 @@ { - "$version": "78.1.0", + "$version": "79.0.0", "PropertyTypes": { "AWS::Kendra::DataSource.AccessControlListConfiguration": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-kendra-datasource-accesscontrollistconfiguration.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Kinesis.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Kinesis.json index ba353db9557ef..7e8f71cabc844 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Kinesis.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Kinesis.json @@ -1,5 +1,5 @@ { - "$version": "78.1.0", + "$version": "79.0.0", "PropertyTypes": { "AWS::Kinesis::Stream.StreamEncryption": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-kinesis-stream-streamencryption.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_KinesisAnalytics.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_KinesisAnalytics.json index 3c54d54eb6574..6a9c76a5331c3 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_KinesisAnalytics.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_KinesisAnalytics.json @@ -1,5 +1,5 @@ { - "$version": "78.1.0", + "$version": "79.0.0", "PropertyTypes": { "AWS::KinesisAnalytics::Application.CSVMappingParameters": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-kinesisanalytics-application-csvmappingparameters.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_KinesisAnalyticsV2.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_KinesisAnalyticsV2.json index 1750c9124ed01..e4c5aa3516a87 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_KinesisAnalyticsV2.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_KinesisAnalyticsV2.json @@ -1,5 +1,5 @@ { - "$version": "78.1.0", + "$version": "79.0.0", "PropertyTypes": { "AWS::KinesisAnalyticsV2::Application.ApplicationCodeConfiguration": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-kinesisanalyticsv2-application-applicationcodeconfiguration.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_KinesisFirehose.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_KinesisFirehose.json index a7f2dad966220..3864788db9941 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_KinesisFirehose.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_KinesisFirehose.json @@ -1,5 +1,5 @@ { - "$version": "78.1.0", + "$version": "79.0.0", "PropertyTypes": { "AWS::KinesisFirehose::DeliveryStream.AmazonopensearchserviceBufferingHints": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-kinesisfirehose-deliverystream-amazonopensearchservicebufferinghints.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_KinesisVideo.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_KinesisVideo.json index e76ad535a76a3..ec29975cc4993 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_KinesisVideo.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_KinesisVideo.json @@ -1,5 +1,5 @@ { - "$version": "78.1.0", + "$version": "79.0.0", "PropertyTypes": {}, "ResourceTypes": { "AWS::KinesisVideo::SignalingChannel": { diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_LakeFormation.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_LakeFormation.json index 4d163f56395bb..4a70da81e6b45 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_LakeFormation.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_LakeFormation.json @@ -1,5 +1,5 @@ { - "$version": "78.1.0", + "$version": "79.0.0", "PropertyTypes": { "AWS::LakeFormation::DataCellsFilter.ColumnWildcard": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-lakeformation-datacellsfilter-columnwildcard.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Lambda.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Lambda.json index ddeca5e92a264..0c1ee7c3aada5 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Lambda.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Lambda.json @@ -1,5 +1,5 @@ { - "$version": "78.1.0", + "$version": "79.0.0", "PropertyTypes": { "AWS::Lambda::Alias.AliasRoutingConfiguration": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-lambda-alias-aliasroutingconfiguration.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Lex.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Lex.json index b776803139512..e390b3c7fb096 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Lex.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Lex.json @@ -1,5 +1,5 @@ { - "$version": "78.1.0", + "$version": "79.0.0", "PropertyTypes": { "AWS::Lex::Bot.AdvancedRecognitionSetting": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-lex-bot-advancedrecognitionsetting.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_LicenseManager.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_LicenseManager.json index 8d35e6cba430c..470bcadbe96c0 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_LicenseManager.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_LicenseManager.json @@ -1,5 +1,5 @@ { - "$version": "78.1.0", + "$version": "79.0.0", "PropertyTypes": { "AWS::LicenseManager::License.BorrowConfiguration": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-licensemanager-license-borrowconfiguration.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Lightsail.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Lightsail.json index c018485e7622b..d7402fc99ef42 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Lightsail.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Lightsail.json @@ -1,5 +1,5 @@ { - "$version": "78.1.0", + "$version": "79.0.0", "PropertyTypes": { "AWS::Lightsail::Bucket.AccessRules": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-lightsail-bucket-accessrules.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Location.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Location.json index 317f54e25e129..ec3c01f5a5758 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Location.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Location.json @@ -1,5 +1,5 @@ { - "$version": "78.1.0", + "$version": "79.0.0", "PropertyTypes": { "AWS::Location::Map.MapConfiguration": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-location-map-mapconfiguration.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Logs.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Logs.json index 29d6d8686ab6e..48230c9d94573 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Logs.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Logs.json @@ -1,32 +1,63 @@ { - "$version": "78.1.0", + "$version": "79.0.0", "PropertyTypes": { + "AWS::Logs::MetricFilter.Dimension": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-logs-metricfilter-dimension.html", + "Properties": { + "Key": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-logs-metricfilter-dimension.html#cfn-logs-metricfilter-dimension-key", + "PrimitiveType": "String", + "Required": true, + "UpdateType": "Mutable" + }, + "Value": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-logs-metricfilter-dimension.html#cfn-logs-metricfilter-dimension-value", + "PrimitiveType": "String", + "Required": true, + "UpdateType": "Mutable" + } + } + }, "AWS::Logs::MetricFilter.MetricTransformation": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-logs-metricfilter-metrictransformation.html", "Properties": { "DefaultValue": { - "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-logs-metricfilter-metrictransformation.html#cfn-cwl-metricfilter-metrictransformation-defaultvalue", + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-logs-metricfilter-metrictransformation.html#cfn-logs-metricfilter-metrictransformation-defaultvalue", "PrimitiveType": "Double", "Required": false, "UpdateType": "Mutable" }, + "Dimensions": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-logs-metricfilter-metrictransformation.html#cfn-logs-metricfilter-metrictransformation-dimensions", + "DuplicatesAllowed": false, + "ItemType": "Dimension", + "Required": false, + "Type": "List", + "UpdateType": "Mutable" + }, "MetricName": { - "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-logs-metricfilter-metrictransformation.html#cfn-cwl-metricfilter-metrictransformation-metricname", + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-logs-metricfilter-metrictransformation.html#cfn-logs-metricfilter-metrictransformation-metricname", "PrimitiveType": "String", "Required": true, "UpdateType": "Mutable" }, "MetricNamespace": { - "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-logs-metricfilter-metrictransformation.html#cfn-cwl-metricfilter-metrictransformation-metricnamespace", + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-logs-metricfilter-metrictransformation.html#cfn-logs-metricfilter-metrictransformation-metricnamespace", "PrimitiveType": "String", "Required": true, "UpdateType": "Mutable" }, "MetricValue": { - "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-logs-metricfilter-metrictransformation.html#cfn-cwl-metricfilter-metrictransformation-metricvalue", + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-logs-metricfilter-metrictransformation.html#cfn-logs-metricfilter-metrictransformation-metricvalue", "PrimitiveType": "String", "Required": true, "UpdateType": "Mutable" + }, + "Unit": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-logs-metricfilter-metrictransformation.html#cfn-logs-metricfilter-metrictransformation-unit", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" } } } @@ -122,21 +153,26 @@ "AWS::Logs::MetricFilter": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-logs-metricfilter.html", "Properties": { + "FilterName": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-logs-metricfilter.html#cfn-logs-metricfilter-filtername", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Immutable" + }, "FilterPattern": { - "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-logs-metricfilter.html#cfn-cwl-metricfilter-filterpattern", + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-logs-metricfilter.html#cfn-logs-metricfilter-filterpattern", "PrimitiveType": "String", "Required": true, "UpdateType": "Mutable" }, "LogGroupName": { - "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-logs-metricfilter.html#cfn-cwl-metricfilter-loggroupname", + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-logs-metricfilter.html#cfn-logs-metricfilter-loggroupname", "PrimitiveType": "String", "Required": true, "UpdateType": "Immutable" }, "MetricTransformations": { - "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-logs-metricfilter.html#cfn-cwl-metricfilter-metrictransformations", - "DuplicatesAllowed": false, + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-logs-metricfilter.html#cfn-logs-metricfilter-metrictransformations", "ItemType": "MetricTransformation", "Required": true, "Type": "List", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_LookoutEquipment.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_LookoutEquipment.json index beab3de81a094..ab327233ea251 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_LookoutEquipment.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_LookoutEquipment.json @@ -1,5 +1,5 @@ { - "$version": "78.1.0", + "$version": "79.0.0", "PropertyTypes": {}, "ResourceTypes": { "AWS::LookoutEquipment::InferenceScheduler": { diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_LookoutMetrics.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_LookoutMetrics.json index d71934fe24d1e..61de56dec9a3a 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_LookoutMetrics.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_LookoutMetrics.json @@ -1,5 +1,5 @@ { - "$version": "78.1.0", + "$version": "79.0.0", "PropertyTypes": { "AWS::LookoutMetrics::Alert.Action": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-lookoutmetrics-alert-action.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_LookoutVision.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_LookoutVision.json index b4be479c89e83..99b34c08963b3 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_LookoutVision.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_LookoutVision.json @@ -1,5 +1,5 @@ { - "$version": "78.1.0", + "$version": "79.0.0", "PropertyTypes": {}, "ResourceTypes": { "AWS::LookoutVision::Project": { diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_MSK.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_MSK.json index 71368b7985169..41c545d5a075e 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_MSK.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_MSK.json @@ -1,5 +1,5 @@ { - "$version": "78.1.0", + "$version": "79.0.0", "PropertyTypes": { "AWS::MSK::Cluster.BrokerLogs": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-msk-cluster-brokerlogs.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_MWAA.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_MWAA.json index da34a11ada697..443ddb2a3c25c 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_MWAA.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_MWAA.json @@ -1,5 +1,5 @@ { - "$version": "78.1.0", + "$version": "79.0.0", "PropertyTypes": { "AWS::MWAA::Environment.LoggingConfiguration": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-mwaa-environment-loggingconfiguration.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Macie.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Macie.json index 0a7b69fcf3372..ac371e5bae7cd 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Macie.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Macie.json @@ -1,5 +1,5 @@ { - "$version": "78.1.0", + "$version": "79.0.0", "PropertyTypes": { "AWS::Macie::FindingsFilter.Criterion": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-macie-findingsfilter-criterion.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_ManagedBlockchain.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_ManagedBlockchain.json index 6a2d3f96020ab..f86c8a18c7327 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_ManagedBlockchain.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_ManagedBlockchain.json @@ -1,5 +1,5 @@ { - "$version": "78.1.0", + "$version": "79.0.0", "PropertyTypes": { "AWS::ManagedBlockchain::Member.ApprovalThresholdPolicy": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-managedblockchain-member-approvalthresholdpolicy.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_MediaConnect.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_MediaConnect.json index fbf83505aa9e1..09a7c0bd87528 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_MediaConnect.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_MediaConnect.json @@ -1,5 +1,5 @@ { - "$version": "78.1.0", + "$version": "79.0.0", "PropertyTypes": { "AWS::MediaConnect::Flow.Encryption": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-mediaconnect-flow-encryption.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_MediaConvert.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_MediaConvert.json index c685cff602102..9c5228f25df54 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_MediaConvert.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_MediaConvert.json @@ -1,5 +1,5 @@ { - "$version": "78.1.0", + "$version": "79.0.0", "PropertyTypes": { "AWS::MediaConvert::JobTemplate.AccelerationSettings": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-mediaconvert-jobtemplate-accelerationsettings.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_MediaLive.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_MediaLive.json index 1ba2534971048..c7d06ccbd26dc 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_MediaLive.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_MediaLive.json @@ -1,5 +1,5 @@ { - "$version": "78.1.0", + "$version": "79.0.0", "PropertyTypes": { "AWS::MediaLive::Channel.AacSettings": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-medialive-channel-aacsettings.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_MediaPackage.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_MediaPackage.json index b02ac6d271d5a..d4703fa092a35 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_MediaPackage.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_MediaPackage.json @@ -1,5 +1,5 @@ { - "$version": "78.1.0", + "$version": "79.0.0", "PropertyTypes": { "AWS::MediaPackage::Asset.EgressEndpoint": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-mediapackage-asset-egressendpoint.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_MediaStore.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_MediaStore.json index 6ddb83493c9c9..a4e57efed3aff 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_MediaStore.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_MediaStore.json @@ -1,5 +1,5 @@ { - "$version": "78.1.0", + "$version": "79.0.0", "PropertyTypes": { "AWS::MediaStore::Container.CorsRule": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-mediastore-container-corsrule.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_MediaTailor.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_MediaTailor.json index 87add421abc5a..e45a971bf37b4 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_MediaTailor.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_MediaTailor.json @@ -1,5 +1,5 @@ { - "$version": "78.1.0", + "$version": "79.0.0", "PropertyTypes": { "AWS::MediaTailor::PlaybackConfiguration.AdMarkerPassthrough": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-mediatailor-playbackconfiguration-admarkerpassthrough.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_MemoryDB.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_MemoryDB.json index 63bf1b28f5e00..bed69a50f579e 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_MemoryDB.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_MemoryDB.json @@ -1,5 +1,5 @@ { - "$version": "78.1.0", + "$version": "79.0.0", "PropertyTypes": { "AWS::MemoryDB::Cluster.Endpoint": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-memorydb-cluster-endpoint.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Neptune.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Neptune.json index 3e5bfef6bb76c..e0ab75d05bb7f 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Neptune.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Neptune.json @@ -1,5 +1,5 @@ { - "$version": "78.1.0", + "$version": "79.0.0", "PropertyTypes": { "AWS::Neptune::DBCluster.DBClusterRole": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-neptune-dbcluster-dbclusterrole.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_NetworkFirewall.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_NetworkFirewall.json index c9907ce793514..dfcc67324ed10 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_NetworkFirewall.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_NetworkFirewall.json @@ -1,5 +1,5 @@ { - "$version": "78.1.0", + "$version": "79.0.0", "PropertyTypes": { "AWS::NetworkFirewall::Firewall.SubnetMapping": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-networkfirewall-firewall-subnetmapping.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_NetworkManager.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_NetworkManager.json index 538daece05ec5..8e880a892d99d 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_NetworkManager.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_NetworkManager.json @@ -1,5 +1,5 @@ { - "$version": "78.1.0", + "$version": "79.0.0", "PropertyTypes": { "AWS::NetworkManager::ConnectAttachment.ConnectAttachmentOptions": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-networkmanager-connectattachment-connectattachmentoptions.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_NimbleStudio.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_NimbleStudio.json index 3a921f11edb57..2bc48f41f579f 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_NimbleStudio.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_NimbleStudio.json @@ -1,5 +1,5 @@ { - "$version": "78.1.0", + "$version": "79.0.0", "PropertyTypes": { "AWS::NimbleStudio::LaunchProfile.StreamConfiguration": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-nimblestudio-launchprofile-streamconfiguration.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_OpenSearchService.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_OpenSearchService.json index 2147fd7147331..23905870ffe6f 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_OpenSearchService.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_OpenSearchService.json @@ -1,5 +1,5 @@ { - "$version": "78.1.0", + "$version": "79.0.0", "PropertyTypes": { "AWS::OpenSearchService::Domain.AdvancedSecurityOptionsInput": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-opensearchservice-domain-advancedsecurityoptionsinput.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_OpsWorks.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_OpsWorks.json index b47466192c1b7..89ccd50a7a872 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_OpsWorks.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_OpsWorks.json @@ -1,5 +1,5 @@ { - "$version": "78.1.0", + "$version": "79.0.0", "PropertyTypes": { "AWS::OpsWorks::App.DataSource": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-opsworks-app-datasource.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_OpsWorksCM.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_OpsWorksCM.json index 1b22f52907009..8c3f00980562a 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_OpsWorksCM.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_OpsWorksCM.json @@ -1,5 +1,5 @@ { - "$version": "78.1.0", + "$version": "79.0.0", "PropertyTypes": { "AWS::OpsWorksCM::Server.EngineAttribute": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-opsworkscm-server-engineattribute.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Panorama.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Panorama.json index 9c0c0d85a7b5d..e953759fce108 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Panorama.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Panorama.json @@ -1,5 +1,5 @@ { - "$version": "78.1.0", + "$version": "79.0.0", "PropertyTypes": { "AWS::Panorama::ApplicationInstance.ManifestOverridesPayload": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-panorama-applicationinstance-manifestoverridespayload.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Personalize.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Personalize.json index 932647cf9ea1b..ed131bf1e96cb 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Personalize.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Personalize.json @@ -1,5 +1,5 @@ { - "$version": "78.1.0", + "$version": "79.0.0", "PropertyTypes": { "AWS::Personalize::Dataset.DatasetImportJob": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-personalize-dataset-datasetimportjob.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Pinpoint.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Pinpoint.json index 8c5f75e18d5c7..01f90302a27bb 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Pinpoint.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Pinpoint.json @@ -1,5 +1,5 @@ { - "$version": "78.1.0", + "$version": "79.0.0", "PropertyTypes": { "AWS::Pinpoint::ApplicationSettings.CampaignHook": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-pinpoint-applicationsettings-campaignhook.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_PinpointEmail.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_PinpointEmail.json index 2adde264a65c7..fcb0139a6dea4 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_PinpointEmail.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_PinpointEmail.json @@ -1,5 +1,5 @@ { - "$version": "78.1.0", + "$version": "79.0.0", "PropertyTypes": { "AWS::PinpointEmail::ConfigurationSet.DeliveryOptions": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-pinpointemail-configurationset-deliveryoptions.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_QLDB.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_QLDB.json index 606887a037f9b..5ed73273eeef1 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_QLDB.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_QLDB.json @@ -1,5 +1,5 @@ { - "$version": "78.1.0", + "$version": "79.0.0", "PropertyTypes": { "AWS::QLDB::Stream.KinesisConfiguration": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-qldb-stream-kinesisconfiguration.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_QuickSight.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_QuickSight.json index f1deb4669e886..910156db282f1 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_QuickSight.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_QuickSight.json @@ -1,5 +1,5 @@ { - "$version": "78.1.0", + "$version": "79.0.0", "PropertyTypes": { "AWS::QuickSight::Analysis.AnalysisError": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-quicksight-analysis-analysiserror.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_RAM.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_RAM.json index b8bcd0ea10797..943874ab8ab30 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_RAM.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_RAM.json @@ -1,5 +1,5 @@ { - "$version": "78.1.0", + "$version": "79.0.0", "PropertyTypes": {}, "ResourceTypes": { "AWS::RAM::ResourceShare": { diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_RDS.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_RDS.json index 8d868c32b2642..672685376f286 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_RDS.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_RDS.json @@ -1,5 +1,5 @@ { - "$version": "78.1.0", + "$version": "79.0.0", "PropertyTypes": { "AWS::RDS::DBCluster.DBClusterRole": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-rds-dbcluster-dbclusterrole.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_RUM.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_RUM.json index e2bd9059db9ae..b5c2d79f60840 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_RUM.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_RUM.json @@ -1,5 +1,5 @@ { - "$version": "78.1.0", + "$version": "79.0.0", "PropertyTypes": { "AWS::RUM::AppMonitor.AppMonitorConfiguration": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-rum-appmonitor-appmonitorconfiguration.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Redshift.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Redshift.json index d719a912604f6..c1599ca140cf1 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Redshift.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Redshift.json @@ -1,5 +1,5 @@ { - "$version": "78.1.0", + "$version": "79.0.0", "PropertyTypes": { "AWS::Redshift::Cluster.Endpoint": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-redshift-cluster-endpoint.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_RedshiftServerless.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_RedshiftServerless.json index 1ddea2f7fef9b..c59207137f7c0 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_RedshiftServerless.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_RedshiftServerless.json @@ -1,6 +1,24 @@ { - "$version": "78.1.0", - "PropertyTypes": {}, + "$version": "79.0.0", + "PropertyTypes": { + "AWS::RedshiftServerless::Workgroup.ConfigParameter": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-redshiftserverless-workgroup-configparameter.html", + "Properties": { + "ParameterKey": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-redshiftserverless-workgroup-configparameter.html#cfn-redshiftserverless-workgroup-configparameter-parameterkey", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + }, + "ParameterValue": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-redshiftserverless-workgroup-configparameter.html#cfn-redshiftserverless-workgroup-configparameter-parametervalue", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + } + } + } + }, "ResourceTypes": { "AWS::RedshiftServerless::Namespace": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-redshiftserverless-namespace.html", @@ -75,6 +93,70 @@ "UpdateType": "Immutable" } } + }, + "AWS::RedshiftServerless::Workgroup": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-redshiftserverless-workgroup.html", + "Properties": { + "BaseCapacity": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-redshiftserverless-workgroup.html#cfn-redshiftserverless-workgroup-basecapacity", + "PrimitiveType": "Integer", + "Required": false, + "UpdateType": "Mutable" + }, + "ConfigParameters": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-redshiftserverless-workgroup.html#cfn-redshiftserverless-workgroup-configparameters", + "DuplicatesAllowed": false, + "ItemType": "ConfigParameter", + "Required": false, + "Type": "List", + "UpdateType": "Mutable" + }, + "EnhancedVpcRouting": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-redshiftserverless-workgroup.html#cfn-redshiftserverless-workgroup-enhancedvpcrouting", + "PrimitiveType": "Boolean", + "Required": false, + "UpdateType": "Mutable" + }, + "NamespaceName": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-redshiftserverless-workgroup.html#cfn-redshiftserverless-workgroup-namespacename", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Immutable" + }, + "PubliclyAccessible": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-redshiftserverless-workgroup.html#cfn-redshiftserverless-workgroup-publiclyaccessible", + "PrimitiveType": "Boolean", + "Required": false, + "UpdateType": "Mutable" + }, + "SecurityGroupIds": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-redshiftserverless-workgroup.html#cfn-redshiftserverless-workgroup-securitygroupids", + "PrimitiveItemType": "String", + "Required": false, + "Type": "List", + "UpdateType": "Mutable" + }, + "SubnetIds": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-redshiftserverless-workgroup.html#cfn-redshiftserverless-workgroup-subnetids", + "PrimitiveItemType": "String", + "Required": false, + "Type": "List", + "UpdateType": "Mutable" + }, + "Tags": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-redshiftserverless-workgroup.html#cfn-redshiftserverless-workgroup-tags", + "ItemType": "Tag", + "Required": false, + "Type": "List", + "UpdateType": "Mutable" + }, + "WorkgroupName": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-redshiftserverless-workgroup.html#cfn-redshiftserverless-workgroup-workgroupname", + "PrimitiveType": "String", + "Required": true, + "UpdateType": "Immutable" + } + } } } } diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_RefactorSpaces.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_RefactorSpaces.json index d3a4ada733fff..b55e492a7abdb 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_RefactorSpaces.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_RefactorSpaces.json @@ -1,5 +1,5 @@ { - "$version": "78.1.0", + "$version": "79.0.0", "PropertyTypes": { "AWS::RefactorSpaces::Application.ApiGatewayProxyInput": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-refactorspaces-application-apigatewayproxyinput.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_ResilienceHub.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_ResilienceHub.json index 0b55443ab6a9f..4c6387bfe531a 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_ResilienceHub.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_ResilienceHub.json @@ -1,5 +1,5 @@ { - "$version": "78.1.0", + "$version": "79.0.0", "PropertyTypes": { "AWS::ResilienceHub::App.PhysicalResourceId": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-resiliencehub-app-physicalresourceid.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_ResourceGroups.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_ResourceGroups.json index 0dc8f3ee10f28..2165669a3aeff 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_ResourceGroups.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_ResourceGroups.json @@ -1,5 +1,5 @@ { - "$version": "78.1.0", + "$version": "79.0.0", "PropertyTypes": { "AWS::ResourceGroups::Group.ConfigurationItem": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-resourcegroups-group-configurationitem.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_RoboMaker.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_RoboMaker.json index a3e6a27213f56..3b1dd797a2dc5 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_RoboMaker.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_RoboMaker.json @@ -1,5 +1,5 @@ { - "$version": "78.1.0", + "$version": "79.0.0", "PropertyTypes": { "AWS::RoboMaker::RobotApplication.RobotSoftwareSuite": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-robomaker-robotapplication-robotsoftwaresuite.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Route53.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Route53.json index 98158e6b71aa1..1e657443464b2 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Route53.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Route53.json @@ -1,5 +1,5 @@ { - "$version": "78.1.0", + "$version": "79.0.0", "PropertyTypes": { "AWS::Route53::CidrCollection.Location": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-route53-cidrcollection-location.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Route53RecoveryControl.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Route53RecoveryControl.json index d50fe8a11f057..263fadb3cf72f 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Route53RecoveryControl.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Route53RecoveryControl.json @@ -1,5 +1,5 @@ { - "$version": "78.1.0", + "$version": "79.0.0", "PropertyTypes": { "AWS::Route53RecoveryControl::Cluster.ClusterEndpoint": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-route53recoverycontrol-cluster-clusterendpoint.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Route53RecoveryReadiness.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Route53RecoveryReadiness.json index 1c1d0199c9ce7..5fdd36044dae2 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Route53RecoveryReadiness.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Route53RecoveryReadiness.json @@ -1,5 +1,5 @@ { - "$version": "78.1.0", + "$version": "79.0.0", "PropertyTypes": { "AWS::Route53RecoveryReadiness::ResourceSet.DNSTargetResource": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-route53recoveryreadiness-resourceset-dnstargetresource.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Route53Resolver.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Route53Resolver.json index 2c04c3be029cd..ab191af80629d 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Route53Resolver.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Route53Resolver.json @@ -1,5 +1,5 @@ { - "$version": "78.1.0", + "$version": "79.0.0", "PropertyTypes": { "AWS::Route53Resolver::FirewallRuleGroup.FirewallRule": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-route53resolver-firewallrulegroup-firewallrule.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_S3.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_S3.json index a0401b1a7a1f4..31a2ecb96f91a 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_S3.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_S3.json @@ -1,5 +1,5 @@ { - "$version": "78.1.0", + "$version": "79.0.0", "PropertyTypes": { "AWS::S3::AccessPoint.PublicAccessBlockConfiguration": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-s3-accesspoint-publicaccessblockconfiguration.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_S3ObjectLambda.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_S3ObjectLambda.json index 3bad8c467f0f5..b4f6a0bfb7125 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_S3ObjectLambda.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_S3ObjectLambda.json @@ -1,5 +1,5 @@ { - "$version": "78.1.0", + "$version": "79.0.0", "PropertyTypes": { "AWS::S3ObjectLambda::AccessPoint.ObjectLambdaConfiguration": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-s3objectlambda-accesspoint-objectlambdaconfiguration.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_S3Outposts.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_S3Outposts.json index 07b46da935907..69f6a15d0b906 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_S3Outposts.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_S3Outposts.json @@ -1,5 +1,5 @@ { - "$version": "78.1.0", + "$version": "79.0.0", "PropertyTypes": { "AWS::S3Outposts::AccessPoint.VpcConfiguration": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-s3outposts-accesspoint-vpcconfiguration.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_SDB.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_SDB.json index 5bcb059d217ae..d19b14f81ddcd 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_SDB.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_SDB.json @@ -1,5 +1,5 @@ { - "$version": "78.1.0", + "$version": "79.0.0", "PropertyTypes": {}, "ResourceTypes": { "AWS::SDB::Domain": { diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_SES.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_SES.json index 2bb9718e72e0f..189a56ef7a663 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_SES.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_SES.json @@ -1,5 +1,5 @@ { - "$version": "78.1.0", + "$version": "79.0.0", "PropertyTypes": { "AWS::SES::ConfigurationSet.DeliveryOptions": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-ses-configurationset-deliveryoptions.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_SNS.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_SNS.json index dccf7df6c56dc..b21e9bcbcd360 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_SNS.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_SNS.json @@ -1,5 +1,5 @@ { - "$version": "78.1.0", + "$version": "79.0.0", "PropertyTypes": { "AWS::SNS::Topic.Subscription": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-sns-subscription.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_SQS.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_SQS.json index 1db0d1e5af294..521aa2171a2cd 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_SQS.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_SQS.json @@ -1,5 +1,5 @@ { - "$version": "78.1.0", + "$version": "79.0.0", "PropertyTypes": {}, "ResourceTypes": { "AWS::SQS::Queue": { diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_SSM.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_SSM.json index 018a6296e380f..5c92fde0bf2ef 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_SSM.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_SSM.json @@ -1,5 +1,5 @@ { - "$version": "78.1.0", + "$version": "79.0.0", "PropertyTypes": { "AWS::SSM::Association.InstanceAssociationOutputLocation": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-ssm-association-instanceassociationoutputlocation.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_SSMContacts.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_SSMContacts.json index ea97a917e07eb..88848c2143282 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_SSMContacts.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_SSMContacts.json @@ -1,5 +1,5 @@ { - "$version": "78.1.0", + "$version": "79.0.0", "PropertyTypes": { "AWS::SSMContacts::Contact.ChannelTargetInfo": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-ssmcontacts-contact-channeltargetinfo.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_SSMIncidents.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_SSMIncidents.json index 8a49bb4da5413..f6020ccbd7bec 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_SSMIncidents.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_SSMIncidents.json @@ -1,5 +1,5 @@ { - "$version": "78.1.0", + "$version": "79.0.0", "PropertyTypes": { "AWS::SSMIncidents::ReplicationSet.RegionConfiguration": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-ssmincidents-replicationset-regionconfiguration.html", @@ -95,6 +95,14 @@ "Required": true, "UpdateType": "Mutable" }, + "IncidentTags": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-ssmincidents-responseplan-incidenttemplate.html#cfn-ssmincidents-responseplan-incidenttemplate-incidenttags", + "DuplicatesAllowed": false, + "ItemType": "Tag", + "Required": false, + "Type": "List", + "UpdateType": "Mutable" + }, "NotificationTargets": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-ssmincidents-responseplan-incidenttemplate.html#cfn-ssmincidents-responseplan-incidenttemplate-notificationtargets", "ItemType": "NotificationTargetItem", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_SSO.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_SSO.json index 2017ec4a6c540..6b9c48a537b28 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_SSO.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_SSO.json @@ -1,5 +1,5 @@ { - "$version": "78.1.0", + "$version": "79.0.0", "PropertyTypes": { "AWS::SSO::InstanceAccessControlAttributeConfiguration.AccessControlAttribute": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-sso-instanceaccesscontrolattributeconfiguration-accesscontrolattribute.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_SecretsManager.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_SecretsManager.json index 19d39c0b62436..cf11e2acb54c7 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_SecretsManager.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_SecretsManager.json @@ -1,5 +1,5 @@ { - "$version": "78.1.0", + "$version": "79.0.0", "PropertyTypes": { "AWS::SecretsManager::RotationSchedule.HostedRotationLambda": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-secretsmanager-rotationschedule-hostedrotationlambda.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_SecurityHub.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_SecurityHub.json index 4098b5d9a26b9..1ce471318bad8 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_SecurityHub.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_SecurityHub.json @@ -1,5 +1,5 @@ { - "$version": "78.1.0", + "$version": "79.0.0", "PropertyTypes": {}, "ResourceTypes": { "AWS::SecurityHub::Hub": { diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_ServiceCatalog.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_ServiceCatalog.json index 86845b3d51132..8869a949227ef 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_ServiceCatalog.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_ServiceCatalog.json @@ -1,5 +1,5 @@ { - "$version": "78.1.0", + "$version": "79.0.0", "PropertyTypes": { "AWS::ServiceCatalog::CloudFormationProduct.ProvisioningArtifactProperties": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-servicecatalog-cloudformationproduct-provisioningartifactproperties.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_ServiceCatalogAppRegistry.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_ServiceCatalogAppRegistry.json index cb049588967d4..dd14a62f96b1e 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_ServiceCatalogAppRegistry.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_ServiceCatalogAppRegistry.json @@ -1,5 +1,5 @@ { - "$version": "78.1.0", + "$version": "79.0.0", "PropertyTypes": {}, "ResourceTypes": { "AWS::ServiceCatalogAppRegistry::Application": { diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_ServiceDiscovery.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_ServiceDiscovery.json index a8442b6659f5a..0cd2d41abd2fd 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_ServiceDiscovery.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_ServiceDiscovery.json @@ -1,5 +1,5 @@ { - "$version": "78.1.0", + "$version": "79.0.0", "PropertyTypes": { "AWS::ServiceDiscovery::PrivateDnsNamespace.PrivateDnsPropertiesMutable": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-servicediscovery-privatednsnamespace-privatednspropertiesmutable.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Signer.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Signer.json index c08a34d6fe9fa..60d4ecd85911c 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Signer.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Signer.json @@ -1,5 +1,5 @@ { - "$version": "78.1.0", + "$version": "79.0.0", "PropertyTypes": { "AWS::Signer::SigningProfile.SignatureValidityPeriod": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-signer-signingprofile-signaturevalidityperiod.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_StepFunctions.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_StepFunctions.json index 30f2382d3f54a..a8e22874d220e 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_StepFunctions.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_StepFunctions.json @@ -1,5 +1,5 @@ { - "$version": "78.1.0", + "$version": "79.0.0", "PropertyTypes": { "AWS::StepFunctions::Activity.TagsEntry": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-stepfunctions-activity-tagsentry.html", @@ -181,7 +181,7 @@ }, "DefinitionSubstitutions": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-stepfunctions-statemachine.html#cfn-stepfunctions-statemachine-definitionsubstitutions", - "PrimitiveItemType": "String", + "PrimitiveItemType": "Json", "Required": false, "Type": "Map", "UpdateType": "Mutable" diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Synthetics.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Synthetics.json index 8dbf548910f75..e2735bf2847af 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Synthetics.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Synthetics.json @@ -1,5 +1,5 @@ { - "$version": "78.1.0", + "$version": "79.0.0", "PropertyTypes": { "AWS::Synthetics::Canary.ArtifactConfig": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-synthetics-canary-artifactconfig.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Timestream.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Timestream.json index 3d157b8d81b57..317b7dc70e48e 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Timestream.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Timestream.json @@ -1,5 +1,5 @@ { - "$version": "78.1.0", + "$version": "79.0.0", "PropertyTypes": { "AWS::Timestream::ScheduledQuery.DimensionMapping": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-timestream-scheduledquery-dimensionmapping.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Transfer.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Transfer.json index 9b579b865e558..72388d75454eb 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Transfer.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Transfer.json @@ -1,5 +1,5 @@ { - "$version": "78.1.0", + "$version": "79.0.0", "PropertyTypes": { "AWS::Transfer::Server.EndpointDetails": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-transfer-server-endpointdetails.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_VoiceID.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_VoiceID.json index 7aba4cac54bc3..4d7d1046c5866 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_VoiceID.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_VoiceID.json @@ -1,5 +1,5 @@ { - "$version": "78.1.0", + "$version": "79.0.0", "PropertyTypes": { "AWS::VoiceID::Domain.ServerSideEncryptionConfiguration": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-voiceid-domain-serversideencryptionconfiguration.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_WAF.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_WAF.json index caaabc5659aaf..c42b062e50c49 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_WAF.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_WAF.json @@ -1,5 +1,5 @@ { - "$version": "78.1.0", + "$version": "79.0.0", "PropertyTypes": { "AWS::WAF::ByteMatchSet.ByteMatchTuple": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-waf-bytematchset-bytematchtuples.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_WAFRegional.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_WAFRegional.json index 068a869aebb7d..ca3a90a04572d 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_WAFRegional.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_WAFRegional.json @@ -1,5 +1,5 @@ { - "$version": "78.1.0", + "$version": "79.0.0", "PropertyTypes": { "AWS::WAFRegional::ByteMatchSet.ByteMatchTuple": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-wafregional-bytematchset-bytematchtuple.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_WAFv2.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_WAFv2.json index eec8d9b089db4..60c7976689fad 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_WAFv2.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_WAFv2.json @@ -1,5 +1,5 @@ { - "$version": "78.1.0", + "$version": "79.0.0", "PropertyTypes": { "AWS::WAFv2::LoggingConfiguration.FieldToMatch": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-wafv2-loggingconfiguration-fieldtomatch.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Wisdom.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Wisdom.json index 34195112db532..423f7d7627dd4 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Wisdom.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Wisdom.json @@ -1,5 +1,5 @@ { - "$version": "78.1.0", + "$version": "79.0.0", "PropertyTypes": { "AWS::Wisdom::Assistant.ServerSideEncryptionConfiguration": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-wisdom-assistant-serversideencryptionconfiguration.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_WorkSpaces.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_WorkSpaces.json index f17b5ee99a0a6..47641bb02ecdf 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_WorkSpaces.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_WorkSpaces.json @@ -1,5 +1,5 @@ { - "$version": "78.1.0", + "$version": "79.0.0", "PropertyTypes": { "AWS::WorkSpaces::ConnectionAlias.ConnectionAliasAssociation": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-workspaces-connectionalias-connectionaliasassociation.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_XRay.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_XRay.json index d4a85a6969f97..322a26106ae8c 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_XRay.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_XRay.json @@ -1,5 +1,5 @@ { - "$version": "78.1.0", + "$version": "79.0.0", "PropertyTypes": { "AWS::XRay::Group.InsightsConfiguration": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-xray-group-insightsconfiguration.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_Alexa_ASK.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_Alexa_ASK.json index 90d564154efc8..31769e4638b59 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_Alexa_ASK.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_Alexa_ASK.json @@ -1,5 +1,5 @@ { - "$version": "78.1.0", + "$version": "79.0.0", "PropertyTypes": { "Alexa::ASK::Skill.AuthenticationConfiguration": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-ask-skill-authenticationconfiguration.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_Tag.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_Tag.json index d7c4925aab789..daf652f7cf6ca 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_Tag.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_Tag.json @@ -1,5 +1,5 @@ { - "$version": "78.1.0", + "$version": "79.0.0", "PropertyTypes": { "Tag": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-resource-tags.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/001_Version.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/001_Version.json index 97cb95c77a1d2..2e6d9a42cf80b 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/001_Version.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/001_Version.json @@ -1,3 +1,3 @@ { - "ResourceSpecificationVersion": "78.1.0" + "ResourceSpecificationVersion": "79.0.0" } From 0aad6c988f434403eb2fd946d735d1d40b4a1ca7 Mon Sep 17 00:00:00 2001 From: Rico Huijbers Date: Fri, 8 Jul 2022 15:01:54 +0200 Subject: [PATCH 07/92] fix(iam): `conditions` parameters accept array values (#21009) Because of the type declaration of `Conditions`, which was `{ [key: string]: any }`, and the way TypeScript interprets `any`, it was possible to pass arrays in where maps/objects were expected. Change the type of `Condition` to `unknown`, which makes the type of `Conditions == { [key: string]: unknown }`. This makes TypeScript no longer accept arrays where objects were expected. Would love loved to make the type of `Condition == { [key: string]: unknown }` as well to be even tighter, but apparently we rely on being able to pass `CfnJson` in where a condition goes, which is an object. Closes #20974. ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- .../@aws-cdk/aws-iam/lib/policy-statement.ts | 24 ++++++++--------- packages/@aws-cdk/aws-iam/lib/principals.ts | 26 ++++++++++++++++++- .../@aws-cdk/aws-lambda/lib/function-base.ts | 14 +++++++--- 3 files changed, 47 insertions(+), 17 deletions(-) diff --git a/packages/@aws-cdk/aws-iam/lib/policy-statement.ts b/packages/@aws-cdk/aws-iam/lib/policy-statement.ts index a3b42438f945c..42e415158ec6d 100644 --- a/packages/@aws-cdk/aws-iam/lib/policy-statement.ts +++ b/packages/@aws-cdk/aws-iam/lib/policy-statement.ts @@ -3,7 +3,7 @@ import { IConstruct } from 'constructs'; import { Group } from './group'; import { AccountPrincipal, AccountRootPrincipal, AnyPrincipal, ArnPrincipal, CanonicalUserPrincipal, - FederatedPrincipal, IPrincipal, PrincipalBase, PrincipalPolicyFragment, ServicePrincipal, ServicePrincipalOpts, + FederatedPrincipal, IPrincipal, PrincipalBase, PrincipalPolicyFragment, ServicePrincipal, ServicePrincipalOpts, validateConditionObject, } from './principals'; import { normalizeStatement } from './private/postprocess-policy-document'; import { LITERAL_STRING_KEY, mergePrincipal, sum } from './util'; @@ -380,6 +380,8 @@ export class PolicyStatement { */ public addCondition(key: string, value: Condition) { this.assertNotFrozen('addCondition'); + validateConditionObject(value); + const existingValue = this._condition[key]; this._condition[key] = existingValue ? { ...existingValue, ...value } : value; } @@ -670,19 +672,15 @@ export enum Effect { * Condition for when an IAM policy is in effect. Maps from the keys in a request's context to * a string value or array of string values. See the Conditions interface for more details. */ -export type Condition = any; +export type Condition = unknown; -// NOTE! We'd ideally like to type this as `Record`, because the -// API expects a map which can take either strings or lists of strings. -// -// However, if we were to change this right now, the Java bindings for CDK would -// emit a type of `Map`, but the most common types people would -// instantiate would be an `ImmutableMap` which would not be -// assignable to `Map`. The types don't have a built-in notion -// of co-contravariance, you have to indicate that on the type. So jsii would first -// need to emit the type as `Map`. +// NOTE! We would have liked to have typed this as `Record`, but in some places +// of the code we are assuming we can pass a `CfnJson` object into where a `Condition` is expected, +// and that wouldn't typecheck anymore. // -// Feature request in https://github.com/aws/jsii/issues/1517 +// Needs to be `unknown` instead of `any` so that the type of `Conditions` is +// `Record`; if it had been `Record`, TypeScript would have allowed +// passing an array into `conditions` arguments (where it needs to be a map). /** * Conditions for when an IAM Policy is in effect, specified in the following structure: @@ -877,4 +875,4 @@ class OrderedSet { public direct(): readonly A[] { return this.array; } -} \ No newline at end of file +} diff --git a/packages/@aws-cdk/aws-iam/lib/principals.ts b/packages/@aws-cdk/aws-iam/lib/principals.ts index 27ece9e4d59f5..3a411287d6114 100644 --- a/packages/@aws-cdk/aws-iam/lib/principals.ts +++ b/packages/@aws-cdk/aws-iam/lib/principals.ts @@ -267,8 +267,15 @@ export class PrincipalWithConditions extends PrincipalAdapter { * Add a condition to the principal */ public addCondition(key: string, value: Condition) { + validateConditionObject(value); + const existingValue = this.additionalConditions[key]; - this.additionalConditions[key] = existingValue ? { ...existingValue, ...value } : value; + if (!existingValue) { + this.additionalConditions[key] = value; + } + validateConditionObject(existingValue); + + this.additionalConditions[key] = { ...existingValue, ...value }; } /** @@ -335,6 +342,9 @@ export class PrincipalWithConditions extends PrincipalAdapter { throw new Error(`multiple "${operator}" conditions cannot be merged if one of them contains an unresolved token`); } + validateConditionObject(existing); + validateConditionObject(condition); + mergedConditions[operator] = { ...existing, ...condition }; }); return mergedConditions; @@ -913,3 +923,17 @@ class ServicePrincipalToken implements cdk.IResolvable { return `<${this.service}>`; } } + +/** + * Validate that the given value is a valid Condition object + * + * The type of `Condition` should have been different, but it's too late for that. + * + * Also, the IAM library relies on being able to pass in a `CfnJson` instance for + * a `Condition`. + */ +export function validateConditionObject(x: unknown): asserts x is Record { + if (!x || typeof x !== 'object' || Array.isArray(x)) { + throw new Error('A Condition should be represented as a map of operator to value'); + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-lambda/lib/function-base.ts b/packages/@aws-cdk/aws-lambda/lib/function-base.ts index 14376c3b32909..c26b6b6df38da 100644 --- a/packages/@aws-cdk/aws-lambda/lib/function-base.ts +++ b/packages/@aws-cdk/aws-lambda/lib/function-base.ts @@ -618,9 +618,9 @@ export abstract class FunctionBase extends Resource implements IFunction, ec2.IC if (!conditions) { return undefined; } - const sourceArn = conditions.ArnLike ? conditions.ArnLike['aws:SourceArn'] : undefined; - const sourceAccount = conditions.StringEquals ? conditions.StringEquals['aws:SourceAccount'] : undefined; - const principalOrgID = conditions.StringEquals ? conditions.StringEquals['aws:PrincipalOrgID'] : undefined; + const sourceArn = requireString(requireObject(conditions.ArnLike)?.['aws:SourceArn']); + const sourceAccount = requireString(requireObject(conditions.StringEquals)?.['aws:SourceAccount']); + const principalOrgID = requireString(requireObject(conditions.StringEquals)?.['aws:PrincipalOrgID']); // PrincipalOrgID cannot be combined with any other conditions if (principalOrgID && (sourceArn || sourceAccount)) { @@ -768,3 +768,11 @@ class LatestVersion extends FunctionBase implements IVersion { return addAlias(this, this, aliasName, options); } } + +function requireObject(x: unknown): Record | undefined { + return x && typeof x === 'object' && !Array.isArray(x) ? x as any : undefined; +} + +function requireString(x: unknown): string | undefined { + return x && typeof x === 'string' ? x : undefined; +} \ No newline at end of file From 4e0c80f89353731edc6d5f7aba6539a4f340296c Mon Sep 17 00:00:00 2001 From: Joshua Weber <57131123+daschaa@users.noreply.github.com> Date: Fri, 8 Jul 2022 16:23:05 +0200 Subject: [PATCH 08/92] fix(sns-subscriptions): restrict encryption of queue to only the respective sns topic (under feature flag) (#20521) Implements #20339 The change adds a conditional that only the corresponding SNS Topic can decrypt the SQS queue with the KMS-Key if one was given. ---- ### All Submissions: * [x] Have you followed the guidelines in our [Contributing guide?](https://github.com/aws/aws-cdk/blob/master/CONTRIBUTING.md) ### Adding new Unconventional Dependencies: * [ ] This PR adds new unconventional dependencies following the process described [here](https://github.com/aws/aws-cdk/blob/master/CONTRIBUTING.md/#adding-new-unconventional-dependencies) ### New Features * [x] Have you added the new feature to an [integration test](https://github.com/aws/aws-cdk/blob/master/INTEGRATION_TESTS.md)? * [x] Did you use `yarn integ` to deploy the infrastructure and generate the snapshot (i.e. `yarn integ` without `--dry-run`)? *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- .../@aws-cdk/aws-sns-subscriptions/lib/sqs.ts | 6 +- .../aws-sns-subscriptions/package.json | 1 + .../test/integ.sns-sqs.lit.ts | 12 +- .../aws-cdk-sns-sqs.template.json | 257 +++++++++++------- .../aws-sns-subscriptions/test/subs.test.ts | 153 ++++++++++- packages/@aws-cdk/cx-api/README.md | 19 ++ packages/@aws-cdk/cx-api/lib/features.ts | 12 + 7 files changed, 358 insertions(+), 102 deletions(-) mode change 100644 => 100755 packages/@aws-cdk/aws-sns-subscriptions/test/integ.sns-sqs.lit.ts diff --git a/packages/@aws-cdk/aws-sns-subscriptions/lib/sqs.ts b/packages/@aws-cdk/aws-sns-subscriptions/lib/sqs.ts index 8b8f533ad6788..3d9acff2d2a65 100644 --- a/packages/@aws-cdk/aws-sns-subscriptions/lib/sqs.ts +++ b/packages/@aws-cdk/aws-sns-subscriptions/lib/sqs.ts @@ -1,7 +1,8 @@ import * as iam from '@aws-cdk/aws-iam'; import * as sns from '@aws-cdk/aws-sns'; import * as sqs from '@aws-cdk/aws-sqs'; -import { ArnFormat, Names, Stack, Token } from '@aws-cdk/core'; +import { ArnFormat, FeatureFlags, Names, Stack, Token } from '@aws-cdk/core'; +import * as cxapi from '@aws-cdk/cx-api'; import { Construct } from 'constructs'; import { SubscriptionProps } from './subscription'; @@ -55,6 +56,9 @@ export class SqsSubscription implements sns.ITopicSubscription { resources: ['*'], actions: ['kms:Decrypt', 'kms:GenerateDataKey'], principals: [snsServicePrincipal], + conditions: FeatureFlags.of(topic).isEnabled(cxapi.SNS_SUBSCRIPTIONS_SQS_DECRYPTION_POLICY) + ? { ArnEquals: { 'aws:SourceArn': topic.topicArn } } + : undefined, })); } diff --git a/packages/@aws-cdk/aws-sns-subscriptions/package.json b/packages/@aws-cdk/aws-sns-subscriptions/package.json index a5477a14400ca..5c8d4c76df917 100644 --- a/packages/@aws-cdk/aws-sns-subscriptions/package.json +++ b/packages/@aws-cdk/aws-sns-subscriptions/package.json @@ -87,6 +87,7 @@ "@aws-cdk/aws-sns": "0.0.0", "@aws-cdk/aws-sqs": "0.0.0", "@aws-cdk/core": "0.0.0", + "@aws-cdk/cx-api": "0.0.0", "constructs": "^10.0.0" }, "homepage": "https://github.com/aws/aws-cdk", diff --git a/packages/@aws-cdk/aws-sns-subscriptions/test/integ.sns-sqs.lit.ts b/packages/@aws-cdk/aws-sns-subscriptions/test/integ.sns-sqs.lit.ts old mode 100644 new mode 100755 index 4c65c3ca011c7..cb3f4d8e27151 --- a/packages/@aws-cdk/aws-sns-subscriptions/test/integ.sns-sqs.lit.ts +++ b/packages/@aws-cdk/aws-sns-subscriptions/test/integ.sns-sqs.lit.ts @@ -1,15 +1,21 @@ +import * as kms from '@aws-cdk/aws-kms'; import * as sns from '@aws-cdk/aws-sns'; import * as sqs from '@aws-cdk/aws-sqs'; import * as cdk from '@aws-cdk/core'; +import * as cxapi from '@aws-cdk/cx-api'; import * as subs from '../lib'; +const restrictSqsDescryption = { [cxapi.SNS_SUBSCRIPTIONS_SQS_DECRYPTION_POLICY]: true }; + class SnsToSqs extends cdk.Stack { constructor(scope: cdk.App, id: string, props?: cdk.StackProps) { super(scope, id, props); /// !show const topic = new sns.Topic(this, 'MyTopic'); - const queue = new sqs.Queue(this, 'MyQueue'); + const queue = new sqs.Queue(this, 'MyQueue', { + encryptionMasterKey: new kms.Key(this, 'EncryptionMasterKey'), + }); topic.addSubscription(new subs.SqsSubscription(queue, { deadLetterQueue: new sqs.Queue(this, 'DeadLetterQueue'), @@ -18,7 +24,9 @@ class SnsToSqs extends cdk.Stack { } } -const app = new cdk.App(); +const app = new cdk.App({ + context: restrictSqsDescryption, +}); new SnsToSqs(app, 'aws-cdk-sns-sqs'); diff --git a/packages/@aws-cdk/aws-sns-subscriptions/test/sns-sqs.lit.integ.snapshot/aws-cdk-sns-sqs.template.json b/packages/@aws-cdk/aws-sns-subscriptions/test/sns-sqs.lit.integ.snapshot/aws-cdk-sns-sqs.template.json index 4d4d40244ceae..20a1f73ef916f 100644 --- a/packages/@aws-cdk/aws-sns-subscriptions/test/sns-sqs.lit.integ.snapshot/aws-cdk-sns-sqs.template.json +++ b/packages/@aws-cdk/aws-sns-subscriptions/test/sns-sqs.lit.integ.snapshot/aws-cdk-sns-sqs.template.json @@ -1,110 +1,171 @@ { - "Resources": { - "MyTopic86869434": { - "Type": "AWS::SNS::Topic" - }, - "MyQueueE6CA6235": { - "Type": "AWS::SQS::Queue", - "UpdateReplacePolicy": "Delete", - "DeletionPolicy": "Delete" - }, - "MyQueuePolicy6BBEDDAC": { - "Type": "AWS::SQS::QueuePolicy", - "Properties": { - "PolicyDocument": { - "Statement": [ - { - "Action": "sqs:SendMessage", - "Condition": { - "ArnEquals": { - "aws:SourceArn": { - "Ref": "MyTopic86869434" - } + "Resources": { + "MyTopic86869434": { + "Type": "AWS::SNS::Topic" + }, + "MyQueueE6CA6235": { + "Type": "AWS::SQS::Queue", + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete", + "Properties": { + "KmsMasterKeyId": { + "Fn::GetAtt": [ + "EncryptionMasterKey5BD393B9", + "Arn" + ] } - }, - "Effect": "Allow", - "Principal": { - "Service": "sns.amazonaws.com" - }, - "Resource": { - "Fn::GetAtt": [ - "MyQueueE6CA6235", - "Arn" - ] - } } - ], - "Version": "2012-10-17" }, - "Queues": [ - { - "Ref": "MyQueueE6CA6235" - } - ] - } - }, - "MyQueueawscdksnssqsMyTopic9361DEA223429051": { - "Type": "AWS::SNS::Subscription", - "Properties": { - "Protocol": "sqs", - "TopicArn": { - "Ref": "MyTopic86869434" - }, - "Endpoint": { - "Fn::GetAtt": [ - "MyQueueE6CA6235", - "Arn" - ] + "MyQueuePolicy6BBEDDAC": { + "Type": "AWS::SQS::QueuePolicy", + "Properties": { + "PolicyDocument": { + "Statement": [ + { + "Action": "sqs:SendMessage", + "Condition": { + "ArnEquals": { + "aws:SourceArn": { + "Ref": "MyTopic86869434" + } + } + }, + "Effect": "Allow", + "Principal": { + "Service": "sns.amazonaws.com" + }, + "Resource": { + "Fn::GetAtt": [ + "MyQueueE6CA6235", + "Arn" + ] + } + } + ], + "Version": "2012-10-17" + }, + "Queues": [ + { + "Ref": "MyQueueE6CA6235" + } + ] + } }, - "RedrivePolicy": { - "deadLetterTargetArn": { - "Fn::GetAtt": [ - "DeadLetterQueue9F481546", - "Arn" - ] - } - } - } - }, - "DeadLetterQueue9F481546": { - "Type": "AWS::SQS::Queue", - "UpdateReplacePolicy": "Delete", - "DeletionPolicy": "Delete" - }, - "DeadLetterQueuePolicyB1FB890C": { - "Type": "AWS::SQS::QueuePolicy", - "Properties": { - "PolicyDocument": { - "Statement": [ - { - "Action": "sqs:SendMessage", - "Condition": { - "ArnEquals": { - "aws:SourceArn": { + "MyQueueawscdksnssqsMyTopic9361DEA223429051": { + "Type": "AWS::SNS::Subscription", + "Properties": { + "Protocol": "sqs", + "TopicArn": { "Ref": "MyTopic86869434" - } + }, + "Endpoint": { + "Fn::GetAtt": [ + "MyQueueE6CA6235", + "Arn" + ] + }, + "RedrivePolicy": { + "deadLetterTargetArn": { + "Fn::GetAtt": [ + "DeadLetterQueue9F481546", + "Arn" + ] + } } - }, - "Effect": "Allow", - "Principal": { - "Service": "sns.amazonaws.com" - }, - "Resource": { - "Fn::GetAtt": [ - "DeadLetterQueue9F481546", - "Arn" + } + }, + "DeadLetterQueue9F481546": { + "Type": "AWS::SQS::Queue", + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" + }, + "DeadLetterQueuePolicyB1FB890C": { + "Type": "AWS::SQS::QueuePolicy", + "Properties": { + "PolicyDocument": { + "Statement": [ + { + "Action": "sqs:SendMessage", + "Condition": { + "ArnEquals": { + "aws:SourceArn": { + "Ref": "MyTopic86869434" + } + } + }, + "Effect": "Allow", + "Principal": { + "Service": "sns.amazonaws.com" + }, + "Resource": { + "Fn::GetAtt": [ + "DeadLetterQueue9F481546", + "Arn" + ] + } + } + ], + "Version": "2012-10-17" + }, + "Queues": [ + { + "Ref": "DeadLetterQueue9F481546" + } ] - } } - ], - "Version": "2012-10-17" }, - "Queues": [ - { - "Ref": "DeadLetterQueue9F481546" - } - ] - } + "EncryptionMasterKey5BD393B9": { + "Type": "AWS::KMS::Key", + "Properties": { + "KeyPolicy": { + "Statement": [ + { + "Action": "kms:*", + "Effect": "Allow", + "Principal": { + "AWS": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":iam::", + { + "Ref": "AWS::AccountId" + }, + ":root" + ] + ] + } + }, + "Resource": "*" + }, + { + "Action": [ + "kms:Decrypt", + "kms:GenerateDataKey" + ], + "Effect": "Allow", + "Principal": { + "Service": "sns.amazonaws.com" + }, + "Condition": { + "ArnEquals": { + "aws:SourceArn": { + "Ref": "MyTopic86869434" + } + } + }, + "Resource": "*" + } + ], + "Version": "2012-10-17" + } + }, + "DeletionPolicy": "Retain", + "UpdateReplacePolicy": "Retain" + } } - } } \ No newline at end of file diff --git a/packages/@aws-cdk/aws-sns-subscriptions/test/subs.test.ts b/packages/@aws-cdk/aws-sns-subscriptions/test/subs.test.ts index 69cb80216d0e0..4eed6baf482e1 100644 --- a/packages/@aws-cdk/aws-sns-subscriptions/test/subs.test.ts +++ b/packages/@aws-cdk/aws-sns-subscriptions/test/subs.test.ts @@ -4,10 +4,11 @@ import * as lambda from '@aws-cdk/aws-lambda'; import * as sns from '@aws-cdk/aws-sns'; import * as sqs from '@aws-cdk/aws-sqs'; import { App, CfnParameter, Duration, RemovalPolicy, Stack, Token } from '@aws-cdk/core'; +import * as cxapi from '@aws-cdk/cx-api'; import * as subs from '../lib'; /* eslint-disable quote-props */ - +const restrictSqsDescryption = { [cxapi.SNS_SUBSCRIPTIONS_SQS_DECRYPTION_POLICY]: true }; let stack: Stack; let topic: sns.Topic; @@ -1042,6 +1043,156 @@ test('encrypted queue subscription', () => { }); }); +describe('Restrict sqs decryption feature flag', () => { + test('Restrict decryption of sqs to sns service principal', () => { + const stackUnderTest = new Stack( + new App(), + ); + const topicUnderTest = new sns.Topic(stackUnderTest, 'MyTopic', { + topicName: 'topicName', + displayName: 'displayName', + }); + const key = new kms.Key(stackUnderTest, 'MyKey', { + removalPolicy: RemovalPolicy.DESTROY, + }); + + const queue = new sqs.Queue(stackUnderTest, 'MyQueue', { + encryptionMasterKey: key, + }); + + topicUnderTest.addSubscription(new subs.SqsSubscription(queue)); + + Template.fromStack(stackUnderTest).templateMatches({ + 'Resources': { + 'MyKey6AB29FA6': { + 'Type': 'AWS::KMS::Key', + 'Properties': { + 'KeyPolicy': { + 'Statement': [ + { + 'Action': 'kms:*', + 'Effect': 'Allow', + 'Principal': { + 'AWS': { + 'Fn::Join': [ + '', + [ + 'arn:', + { + 'Ref': 'AWS::Partition', + }, + ':iam::', + { + 'Ref': 'AWS::AccountId', + }, + ':root', + ], + ], + }, + }, + 'Resource': '*', + }, + { + 'Action': [ + 'kms:Decrypt', + 'kms:GenerateDataKey', + ], + 'Effect': 'Allow', + 'Principal': { + 'Service': 'sns.amazonaws.com', + }, + 'Resource': '*', + }, + ], + 'Version': '2012-10-17', + }, + }, + 'UpdateReplacePolicy': 'Delete', + 'DeletionPolicy': 'Delete', + }, + }, + }); + }); + test('Restrict decryption of sqs to sns topic', () => { + const stackUnderTest = new Stack( + new App({ + context: restrictSqsDescryption, + }), + ); + const topicUnderTest = new sns.Topic(stackUnderTest, 'MyTopic', { + topicName: 'topicName', + displayName: 'displayName', + }); + const key = new kms.Key(stackUnderTest, 'MyKey', { + removalPolicy: RemovalPolicy.DESTROY, + }); + + const queue = new sqs.Queue(stackUnderTest, 'MyQueue', { + encryptionMasterKey: key, + }); + + topicUnderTest.addSubscription(new subs.SqsSubscription(queue)); + + Template.fromStack(stackUnderTest).templateMatches({ + 'Resources': { + 'MyKey6AB29FA6': { + 'Type': 'AWS::KMS::Key', + 'Properties': { + 'KeyPolicy': { + 'Statement': [ + { + 'Action': 'kms:*', + 'Effect': 'Allow', + 'Principal': { + 'AWS': { + 'Fn::Join': [ + '', + [ + 'arn:', + { + 'Ref': 'AWS::Partition', + }, + ':iam::', + { + 'Ref': 'AWS::AccountId', + }, + ':root', + ], + ], + }, + }, + 'Resource': '*', + }, + { + 'Action': [ + 'kms:Decrypt', + 'kms:GenerateDataKey', + ], + 'Effect': 'Allow', + 'Principal': { + 'Service': 'sns.amazonaws.com', + }, + 'Resource': '*', + 'Condition': { + 'ArnEquals': { + 'aws:SourceArn': { + 'Ref': 'MyTopic86869434', + }, + }, + }, + }, + ], + 'Version': '2012-10-17', + }, + }, + 'UpdateReplacePolicy': 'Delete', + 'DeletionPolicy': 'Delete', + }, + }, + }); + }); +}); + test('lambda subscription', () => { const fction = new lambda.Function(stack, 'MyFunc', { runtime: lambda.Runtime.NODEJS_14_X, diff --git a/packages/@aws-cdk/cx-api/README.md b/packages/@aws-cdk/cx-api/README.md index 4e88bdf3776c5..84d45500e0008 100644 --- a/packages/@aws-cdk/cx-api/README.md +++ b/packages/@aws-cdk/cx-api/README.md @@ -39,3 +39,22 @@ _cdk.json_ } ``` +* `@aws-cdk/aws-sns-subscriptions:restrictSqsDescryption` + +Enable this feature flag to restrict the decryption of a SQS queue, which is subscribed to a SNS topic, to +only the topic which it is subscribed to and not the whole SNS service of an account. + +Previously the decryption was only restricted to the SNS service principal. To make the SQS subscription more +secure, it is a good practice to restrict the decryption further and only allow the connected SNS topic to decryption +the subscribed queue. + +_cdk.json_ + +```json +{ + "context": { + "@aws-cdk/aws-sns-subscriptions:restrictSqsDescryption": true + } +} +``` + diff --git a/packages/@aws-cdk/cx-api/lib/features.ts b/packages/@aws-cdk/cx-api/lib/features.ts index fcbb188d57cb5..7790d3e49e679 100644 --- a/packages/@aws-cdk/cx-api/lib/features.ts +++ b/packages/@aws-cdk/cx-api/lib/features.ts @@ -282,6 +282,17 @@ export const CODEPIPELINE_CROSS_ACCOUNT_KEY_ALIAS_STACK_SAFE_RESOURCE_NAME = '@a */ export const S3_CREATE_DEFAULT_LOGGING_POLICY = '@aws-cdk/aws-s3:createDefaultLoggingPolicy'; +/** +* Enable this feature flag to restrict the decryption of a SQS queue, which is subscribed to a SNS topic, to +* only the topic which it is subscribed to and not the whole SNS service of an account. +* +* Previously the decryption was only restricted to the SNS service principal. To make the SQS subscription more +* secure, it is a good practice to restrict the decryption further and only allow the connected SNS topic to decryption +* the subscribed queue. +* +*/ +export const SNS_SUBSCRIPTIONS_SQS_DECRYPTION_POLICY = '@aws-cdk/aws-sns-subscriptions:restrictSqsDescryption'; + /** * Flag values that should apply for new projects * @@ -313,6 +324,7 @@ export const FUTURE_FLAGS: { [key: string]: boolean } = { [VALIDATE_SNAPSHOT_REMOVAL_POLICY]: true, [CODEPIPELINE_CROSS_ACCOUNT_KEY_ALIAS_STACK_SAFE_RESOURCE_NAME]: true, [S3_CREATE_DEFAULT_LOGGING_POLICY]: true, + [SNS_SUBSCRIPTIONS_SQS_DECRYPTION_POLICY]: true, }; /** From 0c31b7fed20a49cacd578d10020c5064af3e0db9 Mon Sep 17 00:00:00 2001 From: Calvin Combs <66279577+comcalvi@users.noreply.github.com> Date: Fri, 8 Jul 2022 17:14:19 -0600 Subject: [PATCH 09/92] fix(custom-resources): Custom resource provider framework not passing `ResponseURL` to user function (#21065) #20889 included a change that broke the custom resource framework by not including the necessary presigned URL. This includes the presigned URL, and fixes the issue. This PR does not include test changes because this is the runtime code. Closes #21058 ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- .../lib/provider-framework/runtime/framework.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/packages/@aws-cdk/custom-resources/lib/provider-framework/runtime/framework.ts b/packages/@aws-cdk/custom-resources/lib/provider-framework/runtime/framework.ts index 97b7c4de61e4b..f9586e032ae5f 100644 --- a/packages/@aws-cdk/custom-resources/lib/provider-framework/runtime/framework.ts +++ b/packages/@aws-cdk/custom-resources/lib/provider-framework/runtime/framework.ts @@ -105,9 +105,7 @@ async function invokeUserFunction(functionArnE // automatically by the JavaScript SDK. const resp = await invokeFunction({ FunctionName: functionArn, - - // Strip 'ResponseURL' -- the downstream CR doesn't need it and can only log it by accident - Payload: JSON.stringify({ ...sanitizedPayload, ResponseURL: undefined }), + Payload: JSON.stringify(sanitizedPayload), }); log('user function response:', resp, typeof(resp)); From f7b25b671003b8d6c7400811484beb4284bebacb Mon Sep 17 00:00:00 2001 From: Calvin Combs <66279577+comcalvi@users.noreply.github.com> Date: Fri, 8 Jul 2022 17:14:19 -0600 Subject: [PATCH 10/92] fix(custom-resources): Custom resource provider framework not passing `ResponseURL` to user function (#21065) #20889 included a change that broke the custom resource framework by not including the necessary presigned URL. This includes the presigned URL, and fixes the issue. This PR does not include test changes because this is the runtime code. Closes #21058 ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- .../lib/provider-framework/runtime/framework.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/packages/@aws-cdk/custom-resources/lib/provider-framework/runtime/framework.ts b/packages/@aws-cdk/custom-resources/lib/provider-framework/runtime/framework.ts index 97b7c4de61e4b..f9586e032ae5f 100644 --- a/packages/@aws-cdk/custom-resources/lib/provider-framework/runtime/framework.ts +++ b/packages/@aws-cdk/custom-resources/lib/provider-framework/runtime/framework.ts @@ -105,9 +105,7 @@ async function invokeUserFunction(functionArnE // automatically by the JavaScript SDK. const resp = await invokeFunction({ FunctionName: functionArn, - - // Strip 'ResponseURL' -- the downstream CR doesn't need it and can only log it by accident - Payload: JSON.stringify({ ...sanitizedPayload, ResponseURL: undefined }), + Payload: JSON.stringify(sanitizedPayload), }); log('user function response:', resp, typeof(resp)); From 689881f3f1156b1085fa809d36830f6616f03e85 Mon Sep 17 00:00:00 2001 From: Calvin Combs Date: Fri, 8 Jul 2022 16:34:53 -0700 Subject: [PATCH 11/92] chore(release): 2.31.1 --- CHANGELOG.v2.alpha.md | 2 ++ CHANGELOG.v2.md | 7 +++++++ version.v2.json | 4 ++-- 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.v2.alpha.md b/CHANGELOG.v2.alpha.md index b1d0f2cdf2dd8..09f00b4150ea4 100644 --- a/CHANGELOG.v2.alpha.md +++ b/CHANGELOG.v2.alpha.md @@ -2,6 +2,8 @@ All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. +### [2.31.1-alpha.0](https://github.com/aws/aws-cdk/compare/v2.31.0-alpha.0...v2.31.1-alpha.0) (2022-07-08) + ## [2.31.0-alpha.0](https://github.com/aws/aws-cdk/compare/v2.30.0-alpha.0...v2.31.0-alpha.0) (2022-07-06) diff --git a/CHANGELOG.v2.md b/CHANGELOG.v2.md index 8ad1410c36bc1..6c87ae5a979a4 100644 --- a/CHANGELOG.v2.md +++ b/CHANGELOG.v2.md @@ -2,6 +2,13 @@ All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. +### [2.31.1](https://github.com/aws/aws-cdk/compare/v2.31.0...v2.31.1) (2022-07-08) + + +### Bug Fixes + +* **custom-resources:** Custom resource provider framework not passing `ResponseURL` to user function ([#21065](https://github.com/aws/aws-cdk/issues/21065)) ([f7b25b6](https://github.com/aws/aws-cdk/commit/f7b25b671003b8d6c7400811484beb4284bebacb)), closes [#21058](https://github.com/aws/aws-cdk/issues/21058) + ## [2.31.0](https://github.com/aws/aws-cdk/compare/v2.30.0...v2.31.0) (2022-07-06) diff --git a/version.v2.json b/version.v2.json index 2e7284fd2b214..da020dcf6c61f 100644 --- a/version.v2.json +++ b/version.v2.json @@ -1,4 +1,4 @@ { - "version": "2.31.0", - "alphaVersion": "2.31.0-alpha.0" + "version": "2.31.1", + "alphaVersion": "2.31.1-alpha.0" } \ No newline at end of file From 6a86fd8d4d806841e599feb8643c2ce5854fc375 Mon Sep 17 00:00:00 2001 From: Calvin Combs Date: Fri, 8 Jul 2022 16:42:17 -0700 Subject: [PATCH 12/92] changelog format --- CHANGELOG.v2.alpha.md | 2 +- CHANGELOG.v2.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.v2.alpha.md b/CHANGELOG.v2.alpha.md index 09f00b4150ea4..ab9b8fa3f9417 100644 --- a/CHANGELOG.v2.alpha.md +++ b/CHANGELOG.v2.alpha.md @@ -2,7 +2,7 @@ All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. -### [2.31.1-alpha.0](https://github.com/aws/aws-cdk/compare/v2.31.0-alpha.0...v2.31.1-alpha.0) (2022-07-08) +## [2.31.1-alpha.0](https://github.com/aws/aws-cdk/compare/v2.31.0-alpha.0...v2.31.1-alpha.0) (2022-07-08) ## [2.31.0-alpha.0](https://github.com/aws/aws-cdk/compare/v2.30.0-alpha.0...v2.31.0-alpha.0) (2022-07-06) diff --git a/CHANGELOG.v2.md b/CHANGELOG.v2.md index 6c87ae5a979a4..cfe56b77ac27f 100644 --- a/CHANGELOG.v2.md +++ b/CHANGELOG.v2.md @@ -2,7 +2,7 @@ All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. -### [2.31.1](https://github.com/aws/aws-cdk/compare/v2.31.0...v2.31.1) (2022-07-08) +## [2.31.1](https://github.com/aws/aws-cdk/compare/v2.31.0...v2.31.1) (2022-07-08) ### Bug Fixes From a1138161ca295ad4a81fe32b51beb82438653144 Mon Sep 17 00:00:00 2001 From: Joshua Weber <57131123+daschaa@users.noreply.github.com> Date: Mon, 11 Jul 2022 11:44:08 +0200 Subject: [PATCH 13/92] feat(neptune): add engine version 1.1.1.0 (#21079) Fixes #20869. Adds new engine version `1.1.1.0` to Neptune. See https://docs.aws.amazon.com/neptune/latest/userguide/engine-releases.html As this is just a new enum value, it would be great if the label `pr-linter/exempt-readme` and `pr-linter/exempt-integ-test` could be added. Thanks! ---- ### All Submissions: * [x] Have you followed the guidelines in our [Contributing guide?](https://github.com/aws/aws-cdk/blob/main/CONTRIBUTING.md) ### Adding new Unconventional Dependencies: * [ ] This PR adds new unconventional dependencies following the process described [here](https://github.com/aws/aws-cdk/blob/main/CONTRIBUTING.md/#adding-new-unconventional-dependencies) ### New Features * [ ] Have you added the new feature to an [integration test](https://github.com/aws/aws-cdk/blob/main/INTEGRATION_TESTS.md)? * [ ] Did you use `yarn integ` to deploy the infrastructure and generate the snapshot (i.e. `yarn integ` without `--dry-run`)? *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- packages/@aws-cdk/aws-neptune/lib/cluster.ts | 4 ++++ .../@aws-cdk/aws-neptune/test/cluster.test.ts | 23 +++++++++++++++++++ 2 files changed, 27 insertions(+) diff --git a/packages/@aws-cdk/aws-neptune/lib/cluster.ts b/packages/@aws-cdk/aws-neptune/lib/cluster.ts index a9303286b3693..eb40b6ec3589b 100644 --- a/packages/@aws-cdk/aws-neptune/lib/cluster.ts +++ b/packages/@aws-cdk/aws-neptune/lib/cluster.ts @@ -54,6 +54,10 @@ export class EngineVersion { * Neptune engine version 1.1.0.0 */ public static readonly V1_1_0_0 = new EngineVersion('1.1.0.0'); + /** + * Neptune engine version 1.1.1.0 + */ + public static readonly V1_1_1_0 = new EngineVersion('1.1.1.0'); /** * Constructor for specifying a custom engine version diff --git a/packages/@aws-cdk/aws-neptune/test/cluster.test.ts b/packages/@aws-cdk/aws-neptune/test/cluster.test.ts index 915fe9cf34d0a..4bf730e8aa8d0 100644 --- a/packages/@aws-cdk/aws-neptune/test/cluster.test.ts +++ b/packages/@aws-cdk/aws-neptune/test/cluster.test.ts @@ -118,6 +118,29 @@ describe('DatabaseCluster', () => { }); }); + test.each([ + ['1.1.1.0', EngineVersion.V1_1_1_0], + ])('can create a cluster for engine version %s', (expected, version) => { + // GIVEN + const stack = testStack(); + const vpc = new ec2.Vpc(stack, 'VPC'); + + // WHEN + new DatabaseCluster(stack, 'Database', { + vpc, + instanceType: InstanceType.R5_LARGE, + engineVersion: version, + }); + + // THEN + Template.fromStack(stack).hasResourceProperties('AWS::Neptune::DBCluster', { + EngineVersion: expected, + DBSubnetGroupName: { Ref: 'DatabaseSubnets3C9252C9' }, + VpcSecurityGroupIds: [{ 'Fn::GetAtt': ['DatabaseSecurityGroup5C91FDCB', 'GroupId'] }], + }); + }); + + test('can create a cluster with imported vpc and security group', () => { // GIVEN const stack = testStack(); From 8fe43d6053b145f6048c5d88391c2c8111346786 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 11 Jul 2022 10:56:34 +0000 Subject: [PATCH 14/92] chore(deps): Bump awscli from 1.25.22 to 1.25.26 in /packages/@aws-cdk/lambda-layer-awscli (#21088) Bumps [awscli](https://github.com/aws/aws-cli) from 1.25.22 to 1.25.26.
Changelog

Sourced from awscli's changelog.

1.25.26

  • api-change:backup: This release adds support for authentication using IAM user identity instead of passed IAM role, identified by excluding the IamRoleArn field in the StartRestoreJob API. This feature applies to only resource clients with a destructive restore nature (e.g. SAP HANA).

1.25.25

  • api-change:chime-sdk-meetings: Adds support for AppKeys and TenantIds in Amazon Chime SDK WebRTC sessions
  • api-change:dms: New api to migrate event subscriptions to event bridge rules
  • api-change:iot: This release adds support to register a CA certificate without having to provide a verification certificate. This also allows multiple AWS accounts to register the same CA in the same region.
  • api-change:iotwireless: Adds 5 APIs: PutPositionConfiguration, GetPositionConfiguration, ListPositionConfigurations, UpdatePosition, GetPosition for the new Positioning Service feature which enables customers to configure solvers to calculate position of LoRaWAN devices, or specify position of LoRaWAN devices & gateways.
  • api-change:sagemaker: Heterogeneous clusters: the ability to launch training jobs with multiple instance types. This enables running component of the training job on the instance type that is most suitable for it. e.g. doing data processing and augmentation on CPU instances and neural network training on GPU instances

1.25.24

  • api-change:cloudformation: My AWS Service (placeholder) - Add a new feature Account-level Targeting for StackSet operation
  • api-change:synthetics: This release introduces Group feature, which enables users to group cross-region canaries.

1.25.23

  • api-change:config: Updating documentation service limits
  • api-change:lexv2-models: Update lexv2-models command to latest version
  • api-change:quicksight: This release allows customers to programmatically create QuickSight accounts with Enterprise and Enterprise + Q editions. It also releases allowlisting domains for embedding QuickSight dashboards at runtime through the embedding APIs.
  • api-change:rds: Adds waiters support for DBCluster.
  • api-change:rolesanywhere: IAM Roles Anywhere allows your workloads such as servers, containers, and applications to obtain temporary AWS credentials and use the same IAM roles and policies that you have configured for your AWS workloads to access AWS resources.
  • api-change:ssm-incidents: Adds support for tagging incident-record on creation by providing incident tags in the template within a response-plan.
Commits
  • fc1c700 Merge branch 'release-1.25.26'
  • 9cbd4e2 Bumping version to 1.25.26
  • 1bdd440 Update changelog based on model updates
  • c6df532 Update lockfile to fix atomicwrite (#7095)
  • 74f16ef Merge branch 'release-1.25.25'
  • 7a2656b Merge branch 'release-1.25.25' into develop
  • 87c506a Bumping version to 1.25.25
  • b88a589 Update changelog based on model updates
  • 94a376e Merge branch 'release-1.25.24' into develop
  • 506bab6 Merge branch 'release-1.25.24'
  • Additional commits viewable in compare view

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=awscli&package-manager=pip&previous-version=1.25.22&new-version=1.25.26)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
--- packages/@aws-cdk/lambda-layer-awscli/layer/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/@aws-cdk/lambda-layer-awscli/layer/requirements.txt b/packages/@aws-cdk/lambda-layer-awscli/layer/requirements.txt index 3f84df604a4a0..87616e7fb081b 100644 --- a/packages/@aws-cdk/lambda-layer-awscli/layer/requirements.txt +++ b/packages/@aws-cdk/lambda-layer-awscli/layer/requirements.txt @@ -1 +1 @@ -awscli==1.25.22 +awscli==1.25.26 From dd9f5c5a008a8e1cc4c07511ad26658efa317992 Mon Sep 17 00:00:00 2001 From: Kaizen Conroy <36202692+kaizencc@users.noreply.github.com> Date: Mon, 11 Jul 2022 15:21:52 -0400 Subject: [PATCH 15/92] fix(cli): revert "fix(cli): format of tags in cdk.json is not validated" (#21092) Reverts aws/aws-cdk#21050 It breaks the CLI integ tests that get run on our internal pipelines. The error message is `Error: tags must be an array of { Tag: string, Value: string } objects`, which comes from the `validateTags` method in #21050. The test runs `cdk deploy --tag key=value` which I was able to test on my own account to verify that that is a valid way of adding tags to a stack. --- packages/aws-cdk/lib/cli.ts | 5 ++-- packages/aws-cdk/lib/util/tags.ts | 20 ---------------- packages/aws-cdk/test/util/tags.test.ts | 31 ------------------------- 3 files changed, 2 insertions(+), 54 deletions(-) delete mode 100644 packages/aws-cdk/lib/util/tags.ts delete mode 100644 packages/aws-cdk/test/util/tags.test.ts diff --git a/packages/aws-cdk/lib/cli.ts b/packages/aws-cdk/lib/cli.ts index 5aaedcc7e5759..4533125708409 100644 --- a/packages/aws-cdk/lib/cli.ts +++ b/packages/aws-cdk/lib/cli.ts @@ -23,7 +23,6 @@ import { data, debug, error, print, setLogLevel, setCI } from '../lib/logging'; import { displayNotices, refreshNotices } from '../lib/notices'; import { Command, Configuration, Settings } from '../lib/settings'; import * as version from '../lib/version'; -import { validateTags } from './util/tags'; // https://github.com/yargs/yargs/issues/1929 // https://github.com/evanw/esbuild/issues/1492 @@ -435,7 +434,7 @@ async function initCommandLine() { force: argv.force, toolkitStackName: toolkitStackName, execute: args.execute, - tags: validateTags(configuration.settings.get(['tags'])), + tags: configuration.settings.get(['tags']), terminationProtection: args.terminationProtection, parameters: { bucketName: configuration.settings.get(['toolkitBucket', 'bucketName']), @@ -465,7 +464,7 @@ async function initCommandLine() { notificationArns: args.notificationArns, requireApproval: configuration.settings.get(['requireApproval']), reuseAssets: args['build-exclude'], - tags: validateTags(configuration.settings.get(['tags'])), + tags: configuration.settings.get(['tags']), execute: args.execute, changeSetName: args.changeSetName, force: args.force, diff --git a/packages/aws-cdk/lib/util/tags.ts b/packages/aws-cdk/lib/util/tags.ts deleted file mode 100644 index 75d3277aff00f..0000000000000 --- a/packages/aws-cdk/lib/util/tags.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { Tag } from '../cdk-toolkit'; - -/** - * Throws an error if tags is neither undefined nor an array of Tags, - * as defined in cdk-toolkit.ts. - * - * It does not attempt to validate the tags themselves. It only validates - * that the objects in the array conform to the Tags interface. - */ -export function validateTags(tags: any): Tag[] | undefined { - const valid = tags === undefined || ( - Array.isArray(tags) && - tags.every(t => typeof(t.Tag) === 'string' && typeof(t.Value) === 'string') - ); - if (valid) { - return tags; - } else { - throw new Error('tags must be an array of { Tag: string, Value: string } objects'); - } -} diff --git a/packages/aws-cdk/test/util/tags.test.ts b/packages/aws-cdk/test/util/tags.test.ts deleted file mode 100644 index 9789eb449300d..0000000000000 --- a/packages/aws-cdk/test/util/tags.test.ts +++ /dev/null @@ -1,31 +0,0 @@ -import { validateTags } from '../../lib/util/tags'; - -test('validateTags does not throw when given undefined', () => { - expect(validateTags(undefined)).toBeUndefined(); -}); - -test('validateTags does not throw when given an empty array', () => { - const tags: any = []; - expect(validateTags(tags)).toBe(tags); -}); - -test('validateTags does not throw when given array of Tag objects', () => { - const tags: any = [{ Tag: 'a', Value: 'b' }]; - expect(validateTags(tags)).toBe(tags); -}); - -test('validateTags throws when given an object', () => { - expect(() => validateTags({ a: 'b' })).toThrow('tags must be'); -}); - -test('validateTags throws when given an array of non-Tag objects', () => { - expect(() => validateTags([{ a: 'b' }])).toThrow('tags must be'); -}); - -test('validateTags throws when Tag is not a string', () => { - expect(() => validateTags([{ Tag: null, Value: 'b' }])).toThrow(); -}); - -test('validateTags throws when Value is not a string', () => { - expect(() => validateTags([{ Tag: 'a', Value: null }])).toThrow(); -}); From bf35048cc5f907c7226f60aa8b3b4b8b500d2bc0 Mon Sep 17 00:00:00 2001 From: Joshua Weber <57131123+daschaa@users.noreply.github.com> Date: Tue, 12 Jul 2022 05:11:12 +0200 Subject: [PATCH 16/92] feat(glue): enable partition filtering on tables (#21081) Fixes #20825 Adds `partition_filtering.enabled` to `TableProps` in AWS Glue. ---- ### All Submissions: * [x] Have you followed the guidelines in our [Contributing guide?](https://github.com/aws/aws-cdk/blob/main/CONTRIBUTING.md) ### Adding new Unconventional Dependencies: * [ ] This PR adds new unconventional dependencies following the process described [here](https://github.com/aws/aws-cdk/blob/main/CONTRIBUTING.md/#adding-new-unconventional-dependencies) ### New Features * [x] Have you added the new feature to an [integration test](https://github.com/aws/aws-cdk/blob/main/INTEGRATION_TESTS.md)? * [x] Did you use `yarn integ` to deploy the infrastructure and generate the snapshot (i.e. `yarn integ` without `--dry-run`)? *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- packages/@aws-cdk/aws-glue/README.md | 25 ++++ packages/@aws-cdk/aws-glue/lib/table.ts | 14 ++- .../@aws-cdk/aws-glue/test/integ.table.ts | 8 ++ .../aws-cdk-glue.assets.json | 6 +- .../aws-cdk-glue.template.json | 70 ++++++++++++ .../test/table.integ.snapshot/cdk.out | 2 +- .../test/table.integ.snapshot/integ.json | 4 +- .../test/table.integ.snapshot/manifest.json | 14 ++- .../test/table.integ.snapshot/tree.json | 107 +++++++++++++++++- packages/@aws-cdk/aws-glue/test/table.test.ts | 94 ++++++++++++++- 10 files changed, 333 insertions(+), 11 deletions(-) diff --git a/packages/@aws-cdk/aws-glue/README.md b/packages/@aws-cdk/aws-glue/README.md index 639198c67ed9a..88cf57faaa10b 100644 --- a/packages/@aws-cdk/aws-glue/README.md +++ b/packages/@aws-cdk/aws-glue/README.md @@ -264,6 +264,31 @@ myTable.addPartitionIndex({ }); ``` +### Partition Filtering + +If you have a table with a large number of partitions that grows over time, consider using AWS Glue partition indexing and filtering. + +```ts +declare const myDatabase: glue.Database; +new glue.Table(this, 'MyTable', { + database: myDatabase, + tableName: 'my_table', + columns: [{ + name: 'col1', + type: glue.Schema.STRING, + }], + partitionKeys: [{ + name: 'year', + type: glue.Schema.SMALL_INT, + }, { + name: 'month', + type: glue.Schema.SMALL_INT, + }], + dataFormat: glue.DataFormat.JSON, + enablePartitionFiltering: true, +}); +``` + ## [Encryption](https://docs.aws.amazon.com/athena/latest/ug/encryption.html) You can enable encryption on a Table's data: diff --git a/packages/@aws-cdk/aws-glue/lib/table.ts b/packages/@aws-cdk/aws-glue/lib/table.ts index 1b17da32e5454..ea958879d816b 100644 --- a/packages/@aws-cdk/aws-glue/lib/table.ts +++ b/packages/@aws-cdk/aws-glue/lib/table.ts @@ -172,6 +172,15 @@ export interface TableProps { * @default false */ readonly storedAsSubDirectories?: boolean; + + /** + * Enables partition filtering. + * + * @see https://docs.aws.amazon.com/athena/latest/ug/glue-best-practices.html#glue-best-practices-partition-index + * + * @default - The parameter is not defined + */ + readonly enablePartitionFiltering?: boolean; } /** @@ -302,8 +311,9 @@ export class Table extends Resource implements ITable { partitionKeys: renderColumns(props.partitionKeys), parameters: { - classification: props.dataFormat.classificationString?.value, - has_encrypted_data: this.encryption !== TableEncryption.UNENCRYPTED, + 'classification': props.dataFormat.classificationString?.value, + 'has_encrypted_data': this.encryption !== TableEncryption.UNENCRYPTED, + 'partition_filtering.enabled': props.enablePartitionFiltering, }, storageDescriptor: { location: `s3://${this.bucket.bucketName}/${this.s3Prefix}`, diff --git a/packages/@aws-cdk/aws-glue/test/integ.table.ts b/packages/@aws-cdk/aws-glue/test/integ.table.ts index e9d54d659921e..d9d543a124d16 100644 --- a/packages/@aws-cdk/aws-glue/test/integ.table.ts +++ b/packages/@aws-cdk/aws-glue/test/integ.table.ts @@ -87,6 +87,14 @@ const encryptedTable = new glue.Table(stack, 'MyEncryptedTable', { encryptionKey: new kms.Key(stack, 'MyKey'), }); +new glue.Table(stack, 'MyPartitionFilteredTable', { + database, + tableName: 'partition_filtered_table', + columns, + dataFormat: glue.DataFormat.JSON, + enablePartitionFiltering: true, +}); + const user = new iam.User(stack, 'MyUser'); csvTable.grantReadWrite(user); encryptedTable.grantReadWrite(user); diff --git a/packages/@aws-cdk/aws-glue/test/table.integ.snapshot/aws-cdk-glue.assets.json b/packages/@aws-cdk/aws-glue/test/table.integ.snapshot/aws-cdk-glue.assets.json index 772ec2685ec29..0cd673b0f5be4 100644 --- a/packages/@aws-cdk/aws-glue/test/table.integ.snapshot/aws-cdk-glue.assets.json +++ b/packages/@aws-cdk/aws-glue/test/table.integ.snapshot/aws-cdk-glue.assets.json @@ -1,7 +1,7 @@ { - "version": "17.0.0", + "version": "20.0.0", "files": { - "92638b7a8efe38efd7c845883423f3767018a9e5bd3d67d8d638332f054d0d0f": { + "419b39f03d496de4fb02e795181e9a2ab218fb90bf7a5c9354cf93baa6fea2cf": { "source": { "path": "aws-cdk-glue.template.json", "packaging": "file" @@ -9,7 +9,7 @@ "destinations": { "current_account-current_region": { "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", - "objectKey": "92638b7a8efe38efd7c845883423f3767018a9e5bd3d67d8d638332f054d0d0f.json", + "objectKey": "419b39f03d496de4fb02e795181e9a2ab218fb90bf7a5c9354cf93baa6fea2cf.json", "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" } } diff --git a/packages/@aws-cdk/aws-glue/test/table.integ.snapshot/aws-cdk-glue.template.json b/packages/@aws-cdk/aws-glue/test/table.integ.snapshot/aws-cdk-glue.template.json index 7f5f3e286f0ef..e64e4fe3b8a20 100644 --- a/packages/@aws-cdk/aws-glue/test/table.integ.snapshot/aws-cdk-glue.template.json +++ b/packages/@aws-cdk/aws-glue/test/table.integ.snapshot/aws-cdk-glue.template.json @@ -423,6 +423,76 @@ } } }, + "MyPartitionFilteredTableBucket6ACAA137": { + "Type": "AWS::S3::Bucket", + "UpdateReplacePolicy": "Retain", + "DeletionPolicy": "Retain" + }, + "MyPartitionFilteredTable324BA27A": { + "Type": "AWS::Glue::Table", + "Properties": { + "CatalogId": { + "Ref": "AWS::AccountId" + }, + "DatabaseName": { + "Ref": "MyDatabase1E2517DB" + }, + "TableInput": { + "Description": "partition_filtered_table generated by CDK", + "Name": "partition_filtered_table", + "Parameters": { + "classification": "json", + "has_encrypted_data": false, + "partition_filtering.enabled": true + }, + "StorageDescriptor": { + "Columns": [ + { + "Name": "col1", + "Type": "string" + }, + { + "Comment": "col2 comment", + "Name": "col2", + "Type": "string" + }, + { + "Name": "col3", + "Type": "array" + }, + { + "Name": "col4", + "Type": "map" + }, + { + "Name": "col5", + "Type": "struct" + } + ], + "Compressed": false, + "InputFormat": "org.apache.hadoop.mapred.TextInputFormat", + "Location": { + "Fn::Join": [ + "", + [ + "s3://", + { + "Ref": "MyPartitionFilteredTableBucket6ACAA137" + }, + "/" + ] + ] + }, + "OutputFormat": "org.apache.hadoop.hive.ql.io.HiveIgnoreKeyTextOutputFormat", + "SerdeInfo": { + "SerializationLibrary": "org.openx.data.jsonserde.JsonSerDe" + }, + "StoredAsSubDirectories": false + }, + "TableType": "EXTERNAL_TABLE" + } + } + }, "MyUserDC45028B": { "Type": "AWS::IAM::User" }, diff --git a/packages/@aws-cdk/aws-glue/test/table.integ.snapshot/cdk.out b/packages/@aws-cdk/aws-glue/test/table.integ.snapshot/cdk.out index 90bef2e09ad39..588d7b269d34f 100644 --- a/packages/@aws-cdk/aws-glue/test/table.integ.snapshot/cdk.out +++ b/packages/@aws-cdk/aws-glue/test/table.integ.snapshot/cdk.out @@ -1 +1 @@ -{"version":"17.0.0"} \ No newline at end of file +{"version":"20.0.0"} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-glue/test/table.integ.snapshot/integ.json b/packages/@aws-cdk/aws-glue/test/table.integ.snapshot/integ.json index 85b6fe8295b26..1f604630bc610 100644 --- a/packages/@aws-cdk/aws-glue/test/table.integ.snapshot/integ.json +++ b/packages/@aws-cdk/aws-glue/test/table.integ.snapshot/integ.json @@ -1,7 +1,7 @@ { - "version": "18.0.0", + "version": "20.0.0", "testCases": { - "aws-glue/test/integ.table": { + "integ.table": { "stacks": [ "aws-cdk-glue" ], diff --git a/packages/@aws-cdk/aws-glue/test/table.integ.snapshot/manifest.json b/packages/@aws-cdk/aws-glue/test/table.integ.snapshot/manifest.json index 221b7524100fa..aed4259921a6f 100644 --- a/packages/@aws-cdk/aws-glue/test/table.integ.snapshot/manifest.json +++ b/packages/@aws-cdk/aws-glue/test/table.integ.snapshot/manifest.json @@ -1,5 +1,5 @@ { - "version": "17.0.0", + "version": "20.0.0", "artifacts": { "Tree": { "type": "cdk:tree", @@ -69,6 +69,18 @@ "data": "MyEncryptedTable981A88C6" } ], + "/aws-cdk-glue/MyPartitionFilteredTable/Bucket/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "MyPartitionFilteredTableBucket6ACAA137" + } + ], + "/aws-cdk-glue/MyPartitionFilteredTable/Table": [ + { + "type": "aws:cdk:logicalId", + "data": "MyPartitionFilteredTable324BA27A" + } + ], "/aws-cdk-glue/MyUser/Resource": [ { "type": "aws:cdk:logicalId", diff --git a/packages/@aws-cdk/aws-glue/test/table.integ.snapshot/tree.json b/packages/@aws-cdk/aws-glue/test/table.integ.snapshot/tree.json index 5ec24f621772d..189e9f749a2d6 100644 --- a/packages/@aws-cdk/aws-glue/test/table.integ.snapshot/tree.json +++ b/packages/@aws-cdk/aws-glue/test/table.integ.snapshot/tree.json @@ -9,7 +9,7 @@ "path": "Tree", "constructInfo": { "fqn": "constructs.Construct", - "version": "10.0.9" + "version": "10.1.33" } }, "aws-cdk-glue": { @@ -596,6 +596,111 @@ "version": "0.0.0" } }, + "MyPartitionFilteredTable": { + "id": "MyPartitionFilteredTable", + "path": "aws-cdk-glue/MyPartitionFilteredTable", + "children": { + "Bucket": { + "id": "Bucket", + "path": "aws-cdk-glue/MyPartitionFilteredTable/Bucket", + "children": { + "Resource": { + "id": "Resource", + "path": "aws-cdk-glue/MyPartitionFilteredTable/Bucket/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::S3::Bucket", + "aws:cdk:cloudformation:props": {} + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-s3.CfnBucket", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-s3.Bucket", + "version": "0.0.0" + } + }, + "Table": { + "id": "Table", + "path": "aws-cdk-glue/MyPartitionFilteredTable/Table", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::Glue::Table", + "aws:cdk:cloudformation:props": { + "catalogId": { + "Ref": "AWS::AccountId" + }, + "databaseName": { + "Ref": "MyDatabase1E2517DB" + }, + "tableInput": { + "name": "partition_filtered_table", + "description": "partition_filtered_table generated by CDK", + "parameters": { + "classification": "json", + "has_encrypted_data": false, + "partition_filtering.enabled": true + }, + "storageDescriptor": { + "location": { + "Fn::Join": [ + "", + [ + "s3://", + { + "Ref": "MyPartitionFilteredTableBucket6ACAA137" + }, + "/" + ] + ] + }, + "compressed": false, + "storedAsSubDirectories": false, + "columns": [ + { + "name": "col1", + "type": "string" + }, + { + "name": "col2", + "type": "string", + "comment": "col2 comment" + }, + { + "name": "col3", + "type": "array" + }, + { + "name": "col4", + "type": "map" + }, + { + "name": "col5", + "type": "struct" + } + ], + "inputFormat": "org.apache.hadoop.mapred.TextInputFormat", + "outputFormat": "org.apache.hadoop.hive.ql.io.HiveIgnoreKeyTextOutputFormat", + "serdeInfo": { + "serializationLibrary": "org.openx.data.jsonserde.JsonSerDe" + } + }, + "tableType": "EXTERNAL_TABLE" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-glue.CfnTable", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-glue.Table", + "version": "0.0.0" + } + }, "MyUser": { "id": "MyUser", "path": "aws-cdk-glue/MyUser", diff --git a/packages/@aws-cdk/aws-glue/test/table.test.ts b/packages/@aws-cdk/aws-glue/test/table.test.ts index e3f5df9bb6a3f..79920d73e1a81 100644 --- a/packages/@aws-cdk/aws-glue/test/table.test.ts +++ b/packages/@aws-cdk/aws-glue/test/table.test.ts @@ -1,4 +1,4 @@ -import { Template } from '@aws-cdk/assertions'; +import { Template, Match } from '@aws-cdk/assertions'; import * as iam from '@aws-cdk/aws-iam'; import * as kms from '@aws-cdk/aws-kms'; import * as s3 from '@aws-cdk/aws-s3'; @@ -1567,6 +1567,98 @@ test('Table.fromTableArn', () => { expect(table.tableName).toEqual('tbl1'); }); +test.each([ + ['enabled', true], + ['disabled', false], +])('Partition filtering on table %s', (_, enabled) => { + const app = new cdk.App(); + const dbStack = new cdk.Stack(app, 'db'); + const database = new glue.Database(dbStack, 'Database', { + databaseName: 'database', + }); + + const tableStack = new cdk.Stack(app, 'table'); + new glue.Table(tableStack, 'Table', { + database, + tableName: 'table', + columns: [{ + name: 'col', + type: glue.Schema.STRING, + }], + partitionKeys: [{ + name: 'year', + type: glue.Schema.SMALL_INT, + }], + dataFormat: glue.DataFormat.JSON, + enablePartitionFiltering: enabled, + }); + + Template.fromStack(tableStack).hasResourceProperties('AWS::Glue::Table', { + CatalogId: { + Ref: 'AWS::AccountId', + }, + DatabaseName: { + 'Fn::ImportValue': 'db:ExportsOutputRefDatabaseB269D8BB88F4B1C4', + }, + TableInput: { + Name: 'table', + Description: 'table generated by CDK', + Parameters: { + 'classification': 'json', + 'has_encrypted_data': false, + 'partition_filtering.enabled': enabled, + }, + PartitionKeys: Match.anyValue(), + StorageDescriptor: Match.anyValue(), + TableType: Match.anyValue(), + }, + }); +}); + +test('Partition filtering on table is not defined (default behavior)', () => { + const app = new cdk.App(); + const dbStack = new cdk.Stack(app, 'db'); + const database = new glue.Database(dbStack, 'Database', { + databaseName: 'database', + }); + + const tableStack = new cdk.Stack(app, 'table'); + new glue.Table(tableStack, 'Table', { + database, + tableName: 'table', + columns: [{ + name: 'col', + type: glue.Schema.STRING, + }], + partitionKeys: [{ + name: 'year', + type: glue.Schema.SMALL_INT, + }], + dataFormat: glue.DataFormat.JSON, + enablePartitionFiltering: undefined, + }); + + Template.fromStack(tableStack).hasResourceProperties('AWS::Glue::Table', { + CatalogId: { + Ref: 'AWS::AccountId', + }, + DatabaseName: { + 'Fn::ImportValue': 'db:ExportsOutputRefDatabaseB269D8BB88F4B1C4', + }, + TableInput: { + Name: 'table', + Description: 'table generated by CDK', + Parameters: { + classification: 'json', + has_encrypted_data: false, + }, + PartitionKeys: Match.anyValue(), + StorageDescriptor: Match.anyValue(), + TableType: Match.anyValue(), + }, + }); +}); + function createTable(props: Pick>): void { const stack = new cdk.Stack(); new glue.Table(stack, 'table', { From f71144fcb728e54b541289fabee98484e11ee11a Mon Sep 17 00:00:00 2001 From: Kaizen Conroy <36202692+kaizencc@users.noreply.github.com> Date: Tue, 12 Jul 2022 08:51:36 -0400 Subject: [PATCH 17/92] chore: re-update F# template for new addSubscription signature (#21098) Our build currently breaks because the F# template assumes that addSubscription returns void, which we recently changed. #21038 tried to solve this, but the build failed with a different error. I ran this change through our test pipeline, which succeeded. That is the only reason why I am at all confident in this change. Mostly flying blind in F#... ---- ### All Submissions: * [ ] Have you followed the guidelines in our [Contributing guide?](https://github.com/aws/aws-cdk/blob/main/CONTRIBUTING.md) ### Adding new Unconventional Dependencies: * [ ] This PR adds new unconventional dependencies following the process described [here](https://github.com/aws/aws-cdk/blob/main/CONTRIBUTING.md/#adding-new-unconventional-dependencies) ### New Features * [ ] Have you added the new feature to an [integration test](https://github.com/aws/aws-cdk/blob/main/INTEGRATION_TESTS.md)? * [ ] Did you use `yarn integ` to deploy the infrastructure and generate the snapshot (i.e. `yarn integ` without `--dry-run`)? *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- .../src/%name.PascalCased%/%name.PascalCased%Stack.template.fs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/aws-cdk/lib/init-templates/sample-app/fsharp/src/%name.PascalCased%/%name.PascalCased%Stack.template.fs b/packages/aws-cdk/lib/init-templates/sample-app/fsharp/src/%name.PascalCased%/%name.PascalCased%Stack.template.fs index 74b2c76e2573f..9ad4fac32567c 100644 --- a/packages/aws-cdk/lib/init-templates/sample-app/fsharp/src/%name.PascalCased%/%name.PascalCased%Stack.template.fs +++ b/packages/aws-cdk/lib/init-templates/sample-app/fsharp/src/%name.PascalCased%/%name.PascalCased%Stack.template.fs @@ -11,4 +11,4 @@ type %name.PascalCased%Stack(scope, id, props) as this = let queue = Queue(this, "%name.PascalCased%Queue", QueueProps(VisibilityTimeout = Duration.Seconds(300.))) let topic = Topic(this, "%name.PascalCased%Topic") - topic.AddSubscription(SqsSubscription(queue)) |> ignore + do topic.AddSubscription(SqsSubscription(queue)) |> ignore From b5e9c1a99be6898c544f91781ceb4ee1d371a03e Mon Sep 17 00:00:00 2001 From: Joshua Weber <57131123+daschaa@users.noreply.github.com> Date: Tue, 12 Jul 2022 15:27:51 +0200 Subject: [PATCH 18/92] feat(redshift): adds classic or elastic resize type option (#21084) Fixes #19430. Adds the property `classicResizing` property to the `ClusterProps`. If not set or set to false, elastic resizing is used. I feel like an entry in the README is not necessary. If it is ok, please add `pr-linter/exempt-readme` label to this PR. ---- ### All Submissions: * [x] Have you followed the guidelines in our [Contributing guide?](https://github.com/aws/aws-cdk/blob/main/CONTRIBUTING.md) ### Adding new Unconventional Dependencies: * [ ] This PR adds new unconventional dependencies following the process described [here](https://github.com/aws/aws-cdk/blob/main/CONTRIBUTING.md/#adding-new-unconventional-dependencies) ### New Features * [ ] Have you added the new feature to an [integration test](https://github.com/aws/aws-cdk/blob/main/INTEGRATION_TESTS.md)? * [ ] Did you use `yarn integ` to deploy the infrastructure and generate the snapshot (i.e. `yarn integ` without `--dry-run`)? *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- packages/@aws-cdk/aws-redshift/lib/cluster.ts | 14 ++++ .../aws-redshift/test/cluster.test.ts | 67 +++++++++++++++++++ 2 files changed, 81 insertions(+) diff --git a/packages/@aws-cdk/aws-redshift/lib/cluster.ts b/packages/@aws-cdk/aws-redshift/lib/cluster.ts index ab86540d3d7b3..3a61f3ad93afe 100644 --- a/packages/@aws-cdk/aws-redshift/lib/cluster.ts +++ b/packages/@aws-cdk/aws-redshift/lib/cluster.ts @@ -322,6 +322,19 @@ export interface ClusterProps { * @default false */ readonly publiclyAccessible?: boolean + + /** + * If this flag is set, the cluster resizing type will be set to classic. + * When resizing a cluster, classic resizing will always provision a new cluster and transfer the data there. + * + * Classic resize takes more time to complete, but it can be useful in cases where the change in node count or + * the node type to migrate to doesn't fall within the bounds for elastic resize. + * + * @see https://docs.aws.amazon.com/redshift/latest/mgmt/managing-cluster-operations.html#elastic-resize + * + * @default - Elastic resize type + */ + readonly classicResizing?: boolean } /** @@ -485,6 +498,7 @@ export class Cluster extends ClusterBase { // Encryption kmsKeyId: props.encryptionKey?.keyId, encrypted: props.encrypted ?? true, + classic: props.classicResizing, }); cluster.applyRemovalPolicy(removalPolicy, { diff --git a/packages/@aws-cdk/aws-redshift/test/cluster.test.ts b/packages/@aws-cdk/aws-redshift/test/cluster.test.ts index 6db5f18ade684..80a03cb7ac207 100644 --- a/packages/@aws-cdk/aws-redshift/test/cluster.test.ts +++ b/packages/@aws-cdk/aws-redshift/test/cluster.test.ts @@ -422,6 +422,73 @@ test('default child returns a CfnCluster', () => { expect(cluster.node.defaultChild).toBeInstanceOf(CfnCluster); }); +test.each([ + ['elastic', false], + ['classic', true], +])('resize type (%s)', (_, classicResizing) => { + // WHEN + new Cluster(stack, 'Redshift', { + masterUser: { + masterUsername: 'admin', + masterPassword: cdk.SecretValue.unsafePlainText('tooshort'), + }, + classicResizing, + vpc, + }); + + // THEN + Template.fromStack(stack).hasResource('AWS::Redshift::Cluster', { + Properties: { + AllowVersionUpgrade: true, + MasterUsername: 'admin', + MasterUserPassword: 'tooshort', + ClusterType: 'multi-node', + AutomatedSnapshotRetentionPeriod: 1, + Encrypted: true, + NumberOfNodes: 2, + NodeType: 'dc2.large', + DBName: 'default_db', + PubliclyAccessible: false, + ClusterSubnetGroupName: { Ref: 'RedshiftSubnetsDFE70E0A' }, + VpcSecurityGroupIds: [{ 'Fn::GetAtt': ['RedshiftSecurityGroup796D74A7', 'GroupId'] }], + Classic: classicResizing, + }, + DeletionPolicy: 'Retain', + UpdateReplacePolicy: 'Retain', + }); +}); + +test('resize type not set', () => { + // WHEN + new Cluster(stack, 'Redshift', { + masterUser: { + masterUsername: 'admin', + masterPassword: cdk.SecretValue.unsafePlainText('tooshort'), + }, + vpc, + }); + + // THEN + Template.fromStack(stack).hasResource('AWS::Redshift::Cluster', { + Properties: { + AllowVersionUpgrade: true, + MasterUsername: 'admin', + MasterUserPassword: 'tooshort', + ClusterType: 'multi-node', + AutomatedSnapshotRetentionPeriod: 1, + Encrypted: true, + NumberOfNodes: 2, + NodeType: 'dc2.large', + DBName: 'default_db', + PubliclyAccessible: false, + ClusterSubnetGroupName: { Ref: 'RedshiftSubnetsDFE70E0A' }, + VpcSecurityGroupIds: [{ 'Fn::GetAtt': ['RedshiftSecurityGroup796D74A7', 'GroupId'] }], + }, + DeletionPolicy: 'Retain', + UpdateReplacePolicy: 'Retain', + }); +}); + function testStack() { const newTestStack = new cdk.Stack(undefined, undefined, { env: { account: '12345', region: 'us-test-1' } }); newTestStack.node.setContext('availability-zones:12345:us-test-1', ['us-test-1a', 'us-test-1b']); From 4332e2341a48d5d182c3fa605e43a0d20b0640d0 Mon Sep 17 00:00:00 2001 From: watany <76135106+watany-dev@users.noreply.github.com> Date: Tue, 12 Jul 2022 23:07:08 +0900 Subject: [PATCH 19/92] typo:Lambda Role Principal was a different service (#21106) ---- ### New Features typo: lambda doc. This is a minor fix, but it will behave incorrectly if deployed incorrectly. *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- packages/@aws-cdk/aws-lambda/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/@aws-cdk/aws-lambda/README.md b/packages/@aws-cdk/aws-lambda/README.md index 1db33fbd80631..dc40e77e1363d 100644 --- a/packages/@aws-cdk/aws-lambda/README.md +++ b/packages/@aws-cdk/aws-lambda/README.md @@ -105,7 +105,7 @@ it appropriate permissions: ```ts const myRole = new iam.Role(this, 'My Role', { - assumedBy: new iam.ServicePrincipal('sns.amazonaws.com'), + assumedBy: new iam.ServicePrincipal('lambda.amazonaws.com'), }); const fn = new lambda.Function(this, 'MyFunction', { From c9ed958a6e67bc9f2f59d8d4330e2ceade757962 Mon Sep 17 00:00:00 2001 From: Kaizen Conroy <36202692+kaizencc@users.noreply.github.com> Date: Tue, 12 Jul 2022 11:59:22 -0400 Subject: [PATCH 20/92] chore(mergify): update current members of contribution/core (#21094) * chore: update current members of contribution/core * Update .mergify.yml --- .mergify.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.mergify.yml b/.mergify.yml index 487115e0aee56..c75f34e359160 100644 --- a/.mergify.yml +++ b/.mergify.yml @@ -10,7 +10,7 @@ pull_request_rules: label: add: [ contribution/core ] conditions: - - author~=^(RomainMuller|garnaat|skinny85|rix0rrr|NGL321|Jerry-AWS|MrArnoldPalmer|iliapolo|pkandasamy91|SoManyHs|uttarasridhar|otaviomacedo|madeline-k|kaizencc|comcalvi|Chriscbr|corymhall|peterwoodworth|ryparker|TheRealAmazonKendra|yuth|vinayak-kukreja)$ + - author~=^(RomainMuller|rix0rrr|Jerry-AWS|MrArnoldPalmer|iliapolo|uttarasridhar|otaviomacedo|madeline-k|kaizencc|comcalvi|corymhall|peterwoodworth|ryparker|TheRealAmazonKendra|yuth|vinayak-kukreja|Naumel|mrgrain)$ - -label~="contribution/core" - name: automatic merge actions: From 6e9963c38e091b37a097f176eae2854ab907ae40 Mon Sep 17 00:00:00 2001 From: Calvin Combs <66279577+comcalvi@users.noreply.github.com> Date: Tue, 12 Jul 2022 10:36:35 -0600 Subject: [PATCH 21/92] fix(aws-eks): cap generated stack names at 128 characters (#20528) When imported, Kubectl Providers result in a new nested stack being created. Nested stack names are created by generating a unique ID (which includes the entire construct id path). Kubectl provider ids are constructed the same way (they can't be specified manually, so the code uses a unique ID). This results in the final ID for the stack containing the id of the provider repeated twice. The makes the name hard to decipher, and more importantly can result in stack names being longer than 128 characters. This caps all generated stack names at 128 characters. Ideally, we would be able to change way in which these stack names are generated; however, that would be a breaking change. Fixes #20124. ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- .../@aws-cdk/aws-eks/test/cluster.test.ts | 106 +----------------- packages/@aws-cdk/core/lib/stack.ts | 3 +- packages/@aws-cdk/core/test/stack.test.ts | 11 ++ 3 files changed, 15 insertions(+), 105 deletions(-) diff --git a/packages/@aws-cdk/aws-eks/test/cluster.test.ts b/packages/@aws-cdk/aws-eks/test/cluster.test.ts index bd670b6cc9d61..1f6ba84fc20fa 100644 --- a/packages/@aws-cdk/aws-eks/test/cluster.test.ts +++ b/packages/@aws-cdk/aws-eks/test/cluster.test.ts @@ -91,8 +91,6 @@ describe('cluster', () => { vpcSubnets: [{ subnetType: ec2.SubnetType.PUBLIC }], }); }).toThrow(/Cannot place cluster handler in the VPC since no private subnets could be selected/); - - }); test('throws when provided `clusterHandlerSecurityGroup` without `placeClusterHandlerInVpc: true`', () => { @@ -129,8 +127,6 @@ describe('cluster', () => { privateSubnetIds, isolatedSubnetIds, }); - - }); test('throws if selecting more than one subnet group', () => { @@ -140,8 +136,6 @@ describe('cluster', () => { defaultCapacity: 0, version: eks.KubernetesVersion.V1_21, })).toThrow(/cannot select multiple subnet groups/); - - }); test('synthesis works if only one subnet group is selected', () => { @@ -221,7 +215,6 @@ describe('cluster', () => { const clusterSg = cluster.clusterSecurityGroup; expect(clusterSg.securityGroupId).toEqual(clusterSgId); - }); test('cluster security group is attached when adding self-managed nodes', () => { @@ -351,7 +344,6 @@ describe('cluster', () => { cluster.connectAutoScalingGroupCapacity(selfManaged, { spotInterruptHandler: false }); expect(cluster.node.findAll().filter(c => c.node.id === 'chart-spot-interrupt-handler').length).toEqual(0); - }); test('throws when a non cdk8s chart construct is added as cdk8s chart', () => { @@ -439,8 +431,6 @@ describe('cluster', () => { { 'Fn::GetAtt': ['Cluster9EE0221C', 'ClusterSecurityGroupId'] }, { 'Fn::GetAtt': ['ClusterControlPlaneSecurityGroupD274242C', 'GroupId'] }, ]); - - }); test('can declare a security group from a different stack', () => { @@ -528,8 +518,6 @@ describe('cluster', () => { // make sure we can synth (no circular dependencies between the stacks) app.synth(); - - }); test('can declare a chart with a token from a different stack than the cluster that depends on the cluster stack', () => { @@ -571,8 +559,6 @@ describe('cluster', () => { // make sure we can synth (no circular dependencies between the stacks) app.synth(); - - }); test('can declare a HelmChart in a different stack than the cluster', () => { @@ -605,8 +591,6 @@ describe('cluster', () => { // make sure we can synth (no circular dependencies between the stacks) app.synth(); - - }); test('throws when declaring an ASG role in a different stack than the cluster', () => { @@ -651,8 +635,6 @@ describe('cluster', () => { }).toThrow( 'CapacityStack/autoScaling/InstanceRole should be defined in the scope of the ClusterStack stack to prevent circular dependencies', ); - - }); test('can declare a ServiceAccount in a different stack than the cluster', () => { @@ -683,8 +665,6 @@ describe('cluster', () => { // make sure we can synth (no circular dependencies between the stacks) app.synth(); - - }); test('a default cluster spans all subnets', () => { @@ -741,7 +721,6 @@ describe('cluster', () => { // THEN Template.fromStack(stack).hasResourceProperties('AWS::EC2::VPC', Match.anyValue()); - }); describe('default capacity', () => { @@ -765,7 +744,6 @@ describe('cluster', () => { MinSize: 2, }, }); - }); test('quantity and type can be customized', () => { @@ -790,7 +768,6 @@ describe('cluster', () => { }, }); // expect(stack).toHaveResource('AWS::AutoScaling::LaunchConfiguration', { InstanceType: 'm2.xlarge' })); - }); test('defaultCapacity=0 will not allocate at all', () => { @@ -804,7 +781,6 @@ describe('cluster', () => { expect(cluster.defaultCapacity).toBeUndefined(); Template.fromStack(stack).resourceCountIs('AWS::AutoScaling::AutoScalingGroup', 0); Template.fromStack(stack).resourceCountIs('AWS::AutoScaling::LaunchConfiguration', 0); - }); }); @@ -824,8 +800,6 @@ describe('cluster', () => { { Key: 'Name', Value: 'Stack/VPC/PrivateSubnet1' }, ], }); - - }); test('creating a cluster tags the public VPC subnets', () => { @@ -845,8 +819,6 @@ describe('cluster', () => { { Key: 'Name', Value: 'Stack/VPC/PublicSubnet1' }, ], }); - - }); test('adding capacity creates an ASG without a rolling update policy', () => { @@ -899,8 +871,6 @@ describe('cluster', () => { }, ], }); - - }); test('create nodegroup with existing role', () => { @@ -933,7 +903,6 @@ describe('cluster', () => { MinSize: 10, }, }); - }); test('adding bottlerocket capacity creates an ASG with tags', () => { @@ -967,7 +936,6 @@ describe('cluster', () => { }, ], }); - }); test('adding bottlerocket capacity with bootstrapOptions throws error', () => { @@ -985,7 +953,6 @@ describe('cluster', () => { machineImageType: eks.MachineImageType.BOTTLEROCKET, bootstrapOptions: {}, })).toThrow(/bootstrapOptions is not supported for Bottlerocket/); - }); test('import cluster with existing kubectl provider function', () => { @@ -1133,8 +1100,6 @@ describe('cluster', () => { 'KubectlSubnet0', 'KubectlSubnet1', ]); - - }); test('exercise export/import', () => { @@ -1188,7 +1153,6 @@ describe('cluster', () => { }, }, }); - }); test('mastersRole can be used to map an IAM role to "system:masters"', () => { @@ -1230,8 +1194,6 @@ describe('cluster', () => { ], }, }); - - }); test('addManifest can be used to apply k8s manifests on this cluster', () => { @@ -1256,8 +1218,6 @@ describe('cluster', () => { Template.fromStack(stack).hasResourceProperties(eks.KubernetesManifest.RESOURCE_TYPE, { Manifest: '[{"bar":123},{"boor":[1,2,3]}]', }); - - }); test('kubectl resources can be created in a separate stack', () => { @@ -1293,8 +1253,6 @@ describe('cluster', () => { }, }, }); - - }); test('adding capacity will automatically map its IAM role', () => { @@ -1344,8 +1302,6 @@ describe('cluster', () => { ], }, }); - - }); test('addAutoScalingGroupCapacity will *not* map the IAM role if mapRole is false', () => { @@ -1389,7 +1345,6 @@ describe('cluster', () => { ], }, }); - }); describe('outputs', () => { @@ -1407,7 +1362,6 @@ describe('cluster', () => { ClusterConfigCommand43AAE40F: { Value: { 'Fn::Join': ['', ['aws eks update-kubeconfig --name ', { Ref: 'Cluster9EE0221C' }, ' --region us-east-1 --role-arn ', { 'Fn::GetAtt': ['ClusterMastersRole9AA35625', 'Arn'] }]] } }, ClusterGetTokenCommand06AE992E: { Value: { 'Fn::Join': ['', ['aws eks get-token --cluster-name ', { Ref: 'Cluster9EE0221C' }, ' --region us-east-1 --role-arn ', { 'Fn::GetAtt': ['ClusterMastersRole9AA35625', 'Arn'] }]] } }, }); - }); test('if masters role is defined, it should be included in the config command', () => { @@ -1429,7 +1383,6 @@ describe('cluster', () => { ClusterConfigCommand43AAE40F: { Value: { 'Fn::Join': ['', ['aws eks update-kubeconfig --name ', { Ref: 'Cluster9EE0221C' }, ' --region us-east-1 --role-arn ', { 'Fn::GetAtt': ['masters0D04F23D', 'Arn'] }]] } }, ClusterGetTokenCommand06AE992E: { Value: { 'Fn::Join': ['', ['aws eks get-token --cluster-name ', { Ref: 'Cluster9EE0221C' }, ' --region us-east-1 --role-arn ', { 'Fn::GetAtt': ['masters0D04F23D', 'Arn'] }]] } }, }); - }); test('if `outputConfigCommand=false` will disabled the output', () => { @@ -1449,7 +1402,6 @@ describe('cluster', () => { const assembly = app.synth(); const template = assembly.getStackByName(stack.stackName).template; expect(template.Outputs).toBeUndefined(); // no outputs - }); test('`outputClusterName` can be used to synthesize an output with the cluster name', () => { @@ -1470,7 +1422,6 @@ describe('cluster', () => { expect(template.Outputs).toEqual({ ClusterClusterNameEB26049E: { Value: { Ref: 'Cluster9EE0221C' } }, }); - }); test('`outputMastersRoleArn` can be used to synthesize an output with the arn of the masters role if defined', () => { @@ -1492,7 +1443,6 @@ describe('cluster', () => { expect(template.Outputs).toEqual({ ClusterMastersRoleArnB15964B1: { Value: { 'Fn::GetAtt': ['masters0D04F23D', 'Arn'] } }, }); - }); describe('boostrap user-data', () => { @@ -1509,7 +1459,6 @@ describe('cluster', () => { const template = app.synth().getStackByName(stack.stackName).template; const userData = template.Resources.ClusterMyCapcityLaunchConfig58583345.Properties.UserData; expect(userData).toEqual({ 'Fn::Base64': { 'Fn::Join': ['', ['#!/bin/bash\nset -o xtrace\n/etc/eks/bootstrap.sh ', { Ref: 'Cluster9EE0221C' }, ' --kubelet-extra-args "--node-labels lifecycle=OnDemand" --apiserver-endpoint \'', { 'Fn::GetAtt': ['Cluster9EE0221C', 'Endpoint'] }, '\' --b64-cluster-ca \'', { 'Fn::GetAtt': ['Cluster9EE0221C', 'CertificateAuthorityData'] }, '\' --use-max-pods true\n/opt/aws/bin/cfn-signal --exit-code $? --stack Stack --resource ClusterMyCapcityASGD4CD8B97 --region us-east-1']] } }); - }); test('not rendered if bootstrap is disabled', () => { @@ -1527,7 +1476,6 @@ describe('cluster', () => { const template = app.synth().getStackByName(stack.stackName).template; const userData = template.Resources.ClusterMyCapcityLaunchConfig58583345.Properties.UserData; expect(userData).toEqual({ 'Fn::Base64': '#!/bin/bash' }); - }); // cursory test for options: see test.user-data.ts for full suite @@ -1548,7 +1496,6 @@ describe('cluster', () => { const template = app.synth().getStackByName(stack.stackName).template; const userData = template.Resources.ClusterMyCapcityLaunchConfig58583345.Properties.UserData; expect(userData).toEqual({ 'Fn::Base64': { 'Fn::Join': ['', ['#!/bin/bash\nset -o xtrace\n/etc/eks/bootstrap.sh ', { Ref: 'Cluster9EE0221C' }, ' --kubelet-extra-args "--node-labels lifecycle=OnDemand --node-labels FOO=42" --apiserver-endpoint \'', { 'Fn::GetAtt': ['Cluster9EE0221C', 'Endpoint'] }, '\' --b64-cluster-ca \'', { 'Fn::GetAtt': ['Cluster9EE0221C', 'CertificateAuthorityData'] }, '\' --use-max-pods true\n/opt/aws/bin/cfn-signal --exit-code $? --stack Stack --resource ClusterMyCapcityASGD4CD8B97 --region us-east-1']] } }); - }); describe('spot instances', () => { @@ -1568,7 +1515,6 @@ describe('cluster', () => { const template = app.synth().getStackByName(stack.stackName).template; const userData = template.Resources.ClusterMyCapcityLaunchConfig58583345.Properties.UserData; expect(userData).toEqual({ 'Fn::Base64': { 'Fn::Join': ['', ['#!/bin/bash\nset -o xtrace\n/etc/eks/bootstrap.sh ', { Ref: 'Cluster9EE0221C' }, ' --kubelet-extra-args "--node-labels lifecycle=Ec2Spot --register-with-taints=spotInstance=true:PreferNoSchedule" --apiserver-endpoint \'', { 'Fn::GetAtt': ['Cluster9EE0221C', 'Endpoint'] }, '\' --b64-cluster-ca \'', { 'Fn::GetAtt': ['Cluster9EE0221C', 'CertificateAuthorityData'] }, '\' --use-max-pods true\n/opt/aws/bin/cfn-signal --exit-code $? --stack Stack --resource ClusterMyCapcityASGD4CD8B97 --region us-east-1']] } }); - }); test('interrupt handler is added', () => { @@ -1590,7 +1536,6 @@ describe('cluster', () => { Namespace: 'kube-system', Repository: 'https://aws.github.io/eks-charts', }); - }); test('interrupt handler is not added when spotInterruptHandler is false', () => { @@ -1607,7 +1552,6 @@ describe('cluster', () => { // THEN expect(cluster.node.findAll().filter(c => c.node.id === 'chart-spot-interrupt-handler').length).toEqual(0); - }); test('its possible to add two capacities with spot instances and only one stop handler will be installed', () => { @@ -1628,11 +1572,8 @@ describe('cluster', () => { // THEN Template.fromStack(stack).resourceCountIs(eks.HelmChart.RESOURCE_TYPE, 1); - }); - }); - }); test('if bootstrap is disabled cannot specify options', () => { @@ -1646,7 +1587,6 @@ describe('cluster', () => { bootstrapEnabled: false, bootstrapOptions: { awsApiRetryAttempts: 10 }, })).toThrow(/Cannot specify "bootstrapOptions" if "bootstrapEnabled" is false/); - }); test('EksOptimizedImage() with no nodeType always uses STANDARD with LATEST_KUBERNETES_VERSION', () => { @@ -1668,7 +1608,6 @@ describe('cluster', () => { ([k, v]) => k.startsWith('SsmParameterValueawsserviceeksoptimizedami') && (v as any).Default.includes(LATEST_KUBERNETES_VERSION), )).toEqual(true); - }); test('EksOptimizedImage() with specific kubernetesVersion return correct AMI', () => { @@ -1689,7 +1628,6 @@ describe('cluster', () => { ([k, v]) => k.startsWith('SsmParameterValueawsserviceeksoptimizedami') && (v as any).Default.includes('/1.21/'), )).toEqual(true); - }); test('default cluster capacity with ARM64 instance type comes with nodegroup with correct AmiType', () => { @@ -1708,7 +1646,6 @@ describe('cluster', () => { Template.fromStack(stack).hasResourceProperties('AWS::EKS::Nodegroup', { AmiType: 'AL2_ARM_64', }); - }); test('addNodegroup with ARM64 instance type comes with nodegroup with correct AmiType', () => { @@ -1729,7 +1666,6 @@ describe('cluster', () => { Template.fromStack(stack).hasResourceProperties('AWS::EKS::Nodegroup', { AmiType: 'AL2_ARM_64', }); - }); test('addNodegroupCapacity with T4g instance type comes with nodegroup with correct AmiType', () => { @@ -1750,7 +1686,6 @@ describe('cluster', () => { Template.fromStack(stack).hasResourceProperties('AWS::EKS::Nodegroup', { AmiType: 'AL2_ARM_64', }); - }); test('addAutoScalingGroupCapacity with T4g instance type comes with nodegroup with correct AmiType', () => { @@ -1773,7 +1708,6 @@ describe('cluster', () => { ([k, v]) => k.startsWith('SsmParameterValueawsserviceeksoptimizedami') && (v as any).Default.includes('amazon-linux-2-arm64/'), )).toEqual(true); - }); test('addNodegroupCapacity with C7g instance type comes with nodegroup with correct AmiType', () => { @@ -1839,7 +1773,6 @@ describe('cluster', () => { expect(Object.entries(parameters).some( ([k, v]) => k.startsWith('SsmParameterValueawsserviceeksoptimizedami') && (v as any).Default.includes('amazon-linux-2-gpu'), )).toEqual(true); - }); test('EKS-Optimized AMI with ARM64 when addAutoScalingGroupCapacity', () => { @@ -1861,7 +1794,6 @@ describe('cluster', () => { expect(Object.entries(parameters).some( ([k, v]) => k.startsWith('SsmParameterValueawsserviceeksoptimizedami') && (v as any).Default.includes('/amazon-linux-2-arm64/'), )).toEqual(true); - }); test('BottleRocketImage() with specific kubernetesVersion return correct AMI', () => { @@ -1882,7 +1814,6 @@ describe('cluster', () => { ([k, v]) => k.startsWith('SsmParameterValueawsservicebottlerocketaws') && (v as any).Default.includes('/aws-k8s-1.21/'), )).toEqual(true); - }); test('when using custom resource a creation role & policy is defined', () => { @@ -2048,7 +1979,6 @@ describe('cluster', () => { Version: '2012-10-17', }, }); - }); test('if an explicit cluster name is not provided, the creation role policy is wider (allows interacting with all clusters)', () => { @@ -2122,7 +2052,6 @@ describe('cluster', () => { Version: '2012-10-17', }, }); - }); test('if helm charts are used, the provider role is allowed to assume the creation role', () => { @@ -2234,8 +2163,8 @@ describe('cluster', () => { ], }, }); - }); + test('if openIDConnectProvider a new OpenIDConnectProvider resource is created and exposed', () => { // GIVEN const { stack } = testFixtureNoVpc(); @@ -2266,7 +2195,6 @@ describe('cluster', () => { ], }, }); - }); test('inference instances are supported', () => { // GIVEN @@ -2285,7 +2213,6 @@ describe('cluster', () => { Template.fromStack(stack).hasResourceProperties(eks.KubernetesManifest.RESOURCE_TYPE, { Manifest: JSON.stringify([sanitized]), }); - }); test('kubectl resources are always created after all fargate profiles', () => { @@ -2332,8 +2259,6 @@ describe('cluster', () => { for (const r of kubectlResources) { expect(template.Resources[r].DependsOn).toEqual(['ClusterKubectlReadyBarrier200052AF']); } - - }); test('kubectl provider role can assume creation role', () => { @@ -2464,7 +2389,6 @@ describe('cluster', () => { }, }, }); - }); describe('kubectl provider passes iam role environment to kube ctl lambda', ()=>{ @@ -2502,8 +2426,8 @@ describe('cluster', () => { Ref: 'referencetoStackKubectlIamRole02F8947EArn', }, }); - }); + test('imported cluster', ()=> { const clusterName = 'my-cluster'; @@ -2541,12 +2465,9 @@ describe('cluster', () => { describe('endpoint access', () => { test('public restricted', () => { - expect(() => { eks.EndpointAccess.PUBLIC.onlyFrom('1.2.3.4/32'); }).toThrow(/Cannot restric public access to endpoint when private access is disabled. Use PUBLIC_AND_PRIVATE.onlyFrom\(\) instead./); - - }); test('public non restricted without private subnets', () => { @@ -2566,7 +2487,6 @@ describe('cluster', () => { Template.fromStack(nested).hasResourceProperties('AWS::Lambda::Function', { VpcConfig: Match.absent(), }); - }); test('public non restricted with private subnets', () => { @@ -2599,8 +2519,6 @@ describe('cluster', () => { vpcSubnets: [{ subnetType: ec2.SubnetType.PUBLIC }], }); }).toThrow(/Vpc must contain private subnets when public endpoint access is disabled/); - - }); test('private with private subnets', () => { @@ -2637,7 +2555,6 @@ describe('cluster', () => { Template.fromStack(nested).hasResourceProperties('AWS::Lambda::Function', { VpcConfig: Match.absent(), }); - }); test('private and non restricted public with private subnets', () => { @@ -2668,8 +2585,6 @@ describe('cluster', () => { vpcSubnets: [{ subnetType: ec2.SubnetType.PUBLIC }], }); }).toThrow(/Vpc must contain private subnets when public endpoint access is restricted/); - - }); test('private and restricted public with private subnets', () => { @@ -2687,7 +2602,6 @@ describe('cluster', () => { const functions = Template.fromStack(nested).findResources('AWS::Lambda::Function'); expect(functions.Handler886CB40B.Properties.VpcConfig.SubnetIds.length).not.toEqual(0); expect(functions.Handler886CB40B.Properties.VpcConfig.SecurityGroupIds.length).not.toEqual(0); - }); test('private endpoint access selects only private subnets from looked up vpc', () => { @@ -2873,8 +2787,6 @@ describe('cluster', () => { const template = app.synth().getStackArtifact(stack.stackName).template; expect(template.Resources.Cluster1B02DD5A2.Properties.Config.resourcesVpcConfig.endpointPrivateAccess).toEqual(true); expect(template.Resources.Cluster1B02DD5A2.Properties.Config.resourcesVpcConfig.endpointPublicAccess).toEqual(false); - - }); test('kubectl provider chooses only private subnets', () => { @@ -2933,8 +2845,6 @@ describe('cluster', () => { ], }, }); - - }); test('kubectl provider limits number of subnets to 16', () => { @@ -3055,8 +2965,6 @@ describe('cluster', () => { ], }, }); - - }); test('throw when private access is configured without dns support enabled for the VPC', () => { @@ -3072,7 +2980,6 @@ describe('cluster', () => { prune: false, }); }).toThrow(/Private endpoint access requires the VPC to have DNS support and DNS hostnames enabled/); - }); test('throw when private access is configured without dns hostnames enabled for the VPC', () => { @@ -3088,7 +2995,6 @@ describe('cluster', () => { prune: false, }); }).toThrow(/Private endpoint access requires the VPC to have DNS support and DNS hostnames enabled/); - }); test('throw when cidrs are configured without public access endpoint', () => { @@ -3096,9 +3002,7 @@ describe('cluster', () => { expect(() => { eks.EndpointAccess.PRIVATE.onlyFrom('1.2.3.4/5'); }).toThrow(/CIDR blocks can only be configured when public access is enabled/); - }); - }); test('getServiceLoadBalancerAddress', () => { @@ -3164,8 +3068,6 @@ describe('cluster', () => { Template.fromStack(providerStack).hasResourceProperties('AWS::Lambda::Function', { Layers: ['arn:of:layer'], }); - - }); test('create a cluster using custom resource with secrets encryption using KMS CMK', () => { @@ -3196,7 +3098,6 @@ describe('cluster', () => { }], }, }); - }); test('custom memory size for kubectl provider', () => { @@ -3214,7 +3115,6 @@ describe('cluster', () => { const casm = app.synth(); const providerNestedStackTemplate = JSON.parse(fs.readFileSync(path.join(casm.directory, 'StackawscdkawseksKubectlProvider7346F799.nested.template.json'), 'utf-8')); expect(providerNestedStackTemplate?.Resources?.Handler886CB40B?.Properties?.MemorySize).toEqual(2048); - }); test('custom memory size for imported clusters', () => { @@ -3234,7 +3134,6 @@ describe('cluster', () => { const casm = app.synth(); const providerNestedStackTemplate = JSON.parse(fs.readFileSync(path.join(casm.directory, 'StackStackImported1CBA9C50KubectlProviderAA00BA49.nested.template.json'), 'utf-8')); expect(providerNestedStackTemplate?.Resources?.Handler886CB40B?.Properties?.MemorySize).toEqual(4096); - }); test('create a cluster using custom kubernetes network config', () => { @@ -3256,6 +3155,5 @@ describe('cluster', () => { }, }, }); - }); }); diff --git a/packages/@aws-cdk/core/lib/stack.ts b/packages/@aws-cdk/core/lib/stack.ts index 65298001d5876..23097a90914f2 100644 --- a/packages/@aws-cdk/core/lib/stack.ts +++ b/packages/@aws-cdk/core/lib/stack.ts @@ -1315,7 +1315,7 @@ export function rootPathTo(construct: IConstruct, ancestor?: IConstruct): IConst */ function makeStackName(components: string[]) { if (components.length === 1) { return components[0]; } - return makeUniqueId(components); + return makeUniqueResourceName(components, { maxLength: 128 }); } function getCreateExportsScope(stack: Stack) { @@ -1388,4 +1388,5 @@ import { Token, Tokenization } from './token'; import { referenceNestedStackValueInParent } from './private/refs'; import { Fact, RegionInfo } from '@aws-cdk/region-info'; import { deployTimeLookup } from './private/region-lookup'; +import { makeUniqueResourceName } from './private/unique-resource-name'; diff --git a/packages/@aws-cdk/core/test/stack.test.ts b/packages/@aws-cdk/core/test/stack.test.ts index 48ae7d185d97f..35630f888f8d1 100644 --- a/packages/@aws-cdk/core/test/stack.test.ts +++ b/packages/@aws-cdk/core/test/stack.test.ts @@ -919,6 +919,17 @@ describe('stack', () => { expect(stack.stackName).toEqual('ProdStackD5279B22'); }); + test('generated stack names will not exceed 128 characters', () => { + // WHEN + const root = new App(); + const app = new Construct(root, 'ProdAppThisNameButItWillOnlyBeTooLongWhenCombinedWithTheStackName' + 'z'.repeat(60)); + const stack = new Stack(app, 'ThisNameIsVeryLongButItWillOnlyBeTooLongWhenCombinedWithTheAppNameStack'); + + // THEN + expect(stack.stackName.length).toEqual(128); + expect(stack.stackName).toEqual('ProdAppThisNameButItWillOnlyBeTooLongWhenCombinedWithTheStaceryLongButItWillOnlyBeTooLongWhenCombinedWithTheAppNameStack864CC1D3'); + }); + test('stack construct id does not go through stack name validation if there is an explicit stack name', () => { // GIVEN const app = new App(); From cad6fc5f0f6d963152ede3101d36d085e399f99a Mon Sep 17 00:00:00 2001 From: Otavio Macedo Date: Tue, 12 Jul 2022 18:11:23 +0100 Subject: [PATCH 22/92] fix(cli): `--no-fail` flag is ignored in favor of the `enableDiffNoFail` feature flag (#21107) The CLI doesn't distinguish between `cdk diff --no-fail` and just `cdk diff`. In both cases, it's taking the value of `enableDiffNoFail` feature flag to decide which status code to return. Change the behavior to: | Is there a difference? | `--fail` (CLI option) | `enableDiffNoFail` (FF) | Return code | |------------------------|------------------------|-------------------------|-------------| | No | Don't care | Don't care | 0 | | Yes | false | Don't care | 0 | | Yes | true | Don't care | 1 | | Yes | null | false | 1 | | Yes | null | true | 0 | Fixes https://github.com/aws/aws-cdk/issues/19110. ---- ### All Submissions: * [x] Have you followed the guidelines in our [Contributing guide?](https://github.com/aws/aws-cdk/blob/main/CONTRIBUTING.md) ### Adding new Unconventional Dependencies: * [ ] This PR adds new unconventional dependencies following the process described [here](https://github.com/aws/aws-cdk/blob/main/CONTRIBUTING.md/#adding-new-unconventional-dependencies) ### New Features * [ ] Have you added the new feature to an [integration test](https://github.com/aws/aws-cdk/blob/main/INTEGRATION_TESTS.md)? * [ ] Did you use `yarn integ` to deploy the infrastructure and generate the snapshot (i.e. `yarn integ` without `--dry-run`)? *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- packages/@aws-cdk/cx-api/lib/features.ts | 11 +++++- packages/aws-cdk/lib/cli.ts | 4 +- .../aws-cdk/test/api/deploy-stack.test.ts | 2 +- .../aws-cdk/test/integ/cli/cli.integtest.ts | 37 +++++++++++++++++++ 4 files changed, 49 insertions(+), 5 deletions(-) diff --git a/packages/@aws-cdk/cx-api/lib/features.ts b/packages/@aws-cdk/cx-api/lib/features.ts index 7790d3e49e679..06e4b8e66e87a 100644 --- a/packages/@aws-cdk/cx-api/lib/features.ts +++ b/packages/@aws-cdk/cx-api/lib/features.ts @@ -31,9 +31,16 @@ export const ENABLE_STACK_NAME_DUPLICATES_CONTEXT = '@aws-cdk/core:enableStackNameDuplicates'; /** - * IF this is set, `cdk diff` will always exit with 0. + * Determines what status code `cdk diff` should return when the specified stack + * differs from the deployed stack or the local CloudFormation template: * - * Use `cdk diff --fail` to exit with 1 if there's a diff. + * * aws-cdk:enableDiffNoFail=true => status code == 0 + * * aws-cdk:enableDiffNoFail=false => status code == 1 + * + * You can override this behavior with the --fail flag: + * + * * --fail => status code == 1 + * * --no-fail => status code == 0 */ export const ENABLE_DIFF_NO_FAIL_CONTEXT = 'aws-cdk:enableDiffNoFail'; /** @deprecated use `ENABLE_DIFF_NO_FAIL_CONTEXT` */ diff --git a/packages/aws-cdk/lib/cli.ts b/packages/aws-cdk/lib/cli.ts index 4533125708409..c037a24ba9d33 100644 --- a/packages/aws-cdk/lib/cli.ts +++ b/packages/aws-cdk/lib/cli.ts @@ -227,7 +227,7 @@ async function parseCommandLineArguments() { .option('template', { type: 'string', desc: 'The path to the CloudFormation template to compare with', requiresArg: true }) .option('strict', { type: 'boolean', desc: 'Do not filter out AWS::CDK::Metadata resources', default: false }) .option('security-only', { type: 'boolean', desc: 'Only diff for broadened security changes', default: false }) - .option('fail', { type: 'boolean', desc: 'Fail with exit code 1 in case of diff', default: false })) + .option('fail', { type: 'boolean', desc: 'Fail with exit code 1 in case of diff' })) .command('metadata [STACK]', 'Returns all metadata associated with this stack') .command(['acknowledge [ID]', 'ack [ID]'], 'Acknowledge a notice so that it does not show up anymore') .command('notices', 'Returns a list of relevant notices') @@ -416,7 +416,7 @@ async function initCommandLine() { strict: args.strict, contextLines: args.contextLines, securityOnly: args.securityOnly, - fail: args.fail || !enableDiffNoFail, + fail: args.fail != null ? args.fail : !enableDiffNoFail, stream: args.ci ? process.stdout : undefined, }); diff --git a/packages/aws-cdk/test/api/deploy-stack.test.ts b/packages/aws-cdk/test/api/deploy-stack.test.ts index 77f6c80755ac2..a8642e3d598a4 100644 --- a/packages/aws-cdk/test/api/deploy-stack.test.ts +++ b/packages/aws-cdk/test/api/deploy-stack.test.ts @@ -1,8 +1,8 @@ import { deployStack, DeployStackOptions, ToolkitInfo } from '../../lib/api'; import { tryHotswapDeployment } from '../../lib/api/hotswap-deployments'; +import { setCI } from '../../lib/logging'; import { DEFAULT_FAKE_TEMPLATE, testStack } from '../util'; import { MockedObject, mockResolvedEnvironment, MockSdk, MockSdkProvider, SyncHandlerSubsetOf } from '../util/mock-sdk'; -import { setCI } from '../../lib/logging'; jest.mock('../../lib/api/hotswap-deployments'); diff --git a/packages/aws-cdk/test/integ/cli/cli.integtest.ts b/packages/aws-cdk/test/integ/cli/cli.integtest.ts index 4b9d276eb3496..c2799525a452c 100644 --- a/packages/aws-cdk/test/integ/cli/cli.integtest.ts +++ b/packages/aws-cdk/test/integ/cli/cli.integtest.ts @@ -518,6 +518,43 @@ integTest('cdk diff', withDefaultFixture(async (fixture) => { .rejects.toThrow('exited with error'); })); +integTest('enableDiffNoFail', withDefaultFixture(async (fixture) => { + await diffShouldSucceedWith({ fail: false, enableDiffNoFail: false }); + await diffShouldSucceedWith({ fail: false, enableDiffNoFail: true }); + await diffShouldFailWith({ fail: true, enableDiffNoFail: false }); + await diffShouldFailWith({ fail: true, enableDiffNoFail: true }); + await diffShouldFailWith({ fail: undefined, enableDiffNoFail: false }); + await diffShouldSucceedWith({ fail: undefined, enableDiffNoFail: true }); + + async function diffShouldSucceedWith(props: DiffParameters) { + await expect(diff(props)).resolves.not.toThrowError(); + } + + async function diffShouldFailWith(props: DiffParameters) { + await expect(diff(props)).rejects.toThrow('exited with error'); + } + + async function diff(props: DiffParameters): Promise { + await updateContext(props.enableDiffNoFail); + const flag = props.fail != null + ? (props.fail ? '--fail' : '--no-fail') + : ''; + + return fixture.cdk(['diff', flag, fixture.fullStackName('test-1')]); + } + + async function updateContext(enableDiffNoFail: boolean) { + const cdkJson = JSON.parse(await fs.readFile(path.join(fixture.integTestDir, 'cdk.json'), 'utf8')); + cdkJson.context = { + ...cdkJson.context, + 'aws-cdk:enableDiffNoFail': enableDiffNoFail, + }; + await fs.writeFile(path.join(fixture.integTestDir, 'cdk.json'), JSON.stringify(cdkJson)); + } + + type DiffParameters = { fail?: boolean, enableDiffNoFail: boolean }; +})); + integTest('cdk diff --fail on multiple stacks exits with error if any of the stacks contains a diff', withDefaultFixture(async (fixture) => { // GIVEN const diff1 = await fixture.cdk(['diff', fixture.fullStackName('test-1')]); From a25677bd6d3931b1b522f5ff0859693fe6dc855d Mon Sep 17 00:00:00 2001 From: Jonathan Goldwasser Date: Tue, 12 Jul 2022 19:47:00 +0200 Subject: [PATCH 23/92] feat(backup): vault lock (#21105) AWS Backup Vault Lock enforces a write-once, read-many (WORM) setting for all the backups you store and create in a backup vault. https://docs.aws.amazon.com/aws-backup/latest/devguide/vault-lock.html Closes #21076 ---- ### All Submissions: * [ ] Have you followed the guidelines in our [Contributing guide?](https://github.com/aws/aws-cdk/blob/main/CONTRIBUTING.md) ### Adding new Unconventional Dependencies: * [ ] This PR adds new unconventional dependencies following the process described [here](https://github.com/aws/aws-cdk/blob/main/CONTRIBUTING.md/#adding-new-unconventional-dependencies) ### New Features * [ ] Have you added the new feature to an [integration test](https://github.com/aws/aws-cdk/blob/main/INTEGRATION_TESTS.md)? * [ ] Did you use `yarn integ` to deploy the infrastructure and generate the snapshot (i.e. `yarn integ` without `--dry-run`)? *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- packages/@aws-cdk/aws-backup/README.md | 10 +++ packages/@aws-cdk/aws-backup/lib/vault.ts | 90 ++++++++++++++++++- .../cdk-backup.template.json | 5 +- .../test/backup.integ.snapshot/cdk.out | 2 +- .../test/backup.integ.snapshot/integ.json | 4 +- .../test/backup.integ.snapshot/manifest.json | 2 +- .../test/backup.integ.snapshot/tree.json | 9 +- .../@aws-cdk/aws-backup/test/integ.backup.ts | 5 +- .../@aws-cdk/aws-backup/test/vault.test.ts | 57 +++++++++++- 9 files changed, 173 insertions(+), 11 deletions(-) diff --git a/packages/@aws-cdk/aws-backup/README.md b/packages/@aws-cdk/aws-backup/README.md index 5cf5bc4c1aa1a..687d0b9d800bd 100644 --- a/packages/@aws-cdk/aws-backup/README.md +++ b/packages/@aws-cdk/aws-backup/README.md @@ -176,6 +176,16 @@ backupVault.blockRecoveryPointDeletion(); By default access is not restricted. +Use the `lockConfiguration` property to enable [AWS Backup Vault Lock](https://docs.aws.amazon.com/aws-backup/latest/devguide/vault-lock.html): + +```ts +new BackupVault(stack, 'Vault', { + lockConfiguration: { + minRetention: Duration.days(30), + }, +}); +``` + ## Importing existing backup vault To import an existing backup vault into your CDK application, use the `BackupVault.fromBackupVaultArn` or `BackupVault.fromBackupVaultName` diff --git a/packages/@aws-cdk/aws-backup/lib/vault.ts b/packages/@aws-cdk/aws-backup/lib/vault.ts index 059b6ed6bb17f..001d29db6cd18 100644 --- a/packages/@aws-cdk/aws-backup/lib/vault.ts +++ b/packages/@aws-cdk/aws-backup/lib/vault.ts @@ -1,7 +1,7 @@ import * as iam from '@aws-cdk/aws-iam'; import * as kms from '@aws-cdk/aws-kms'; import * as sns from '@aws-cdk/aws-sns'; -import { ArnFormat, IResource, Lazy, Names, RemovalPolicy, Resource, Stack } from '@aws-cdk/core'; +import { ArnFormat, Duration, IResource, Lazy, Names, RemovalPolicy, Resource, Stack } from '@aws-cdk/core'; import { Construct } from 'constructs'; import { CfnBackupVault } from './backup.generated'; @@ -91,6 +91,15 @@ export interface BackupVaultProps { * @default false */ readonly blockRecoveryPointDeletion?: boolean; + + /** + * Configuration for AWS Backup Vault Lock + * + * @see https://docs.aws.amazon.com/aws-backup/latest/devguide/vault-lock.html + * + * @default - AWS Backup Vault Lock is disabled + */ + readonly lockConfiguration?: LockConfiguration; } /** @@ -129,6 +138,55 @@ export enum BackupVaultEvents { BACKUP_PLAN_MODIFIED = 'BACKUP_PLAN_MODIFIED', } +/** + * Configuration for AWS Backup Vault Lock + * + * @see https://docs.aws.amazon.com/aws-backup/latest/devguide/vault-lock.html + */ +export interface LockConfiguration { + /** + * The minimum retention period that the vault retains its recovery points. + * + * If this parameter is specified, any backup or copy job to the vault must + * have a lifecycle policy with a retention period equal to or longer than + * the minimum retention period. If the job's retention period is shorter than + * that minimum retention period, then the vault fails that backup or copy job, + * and you should either modify your lifecycle settings or use a different + * vault. Recovery points already saved in the vault prior to Vault Lock are + * not affected. + */ + readonly minRetention: Duration; + + /** + * The maximum retention period that the vault retains its recovery points. + * + * If this parameter is specified, any backup or copy job to the vault must + * have a lifecycle policy with a retention period equal to or shorter than + * the maximum retention period. If the job's retention period is longer than + * that maximum retention period, then the vault fails the backup or copy job, + * and you should either modify your lifecycle settings or use a different + * vault. Recovery points already saved in the vault prior to Vault Lock are + * not affected. + * + * @default - Vault Lock does not enforce a maximum retention period + */ + readonly maxRetention?: Duration; + + /** + * The duration before the lock date. + * + * AWS Backup enforces a 72-hour cooling-off period before Vault Lock takes + * effect and becomes immutable. + * + * Before the lock date, you can delete Vault Lock from the vault or change + * the Vault Lock configuration. On and after the lock date, the Vault Lock + * becomes immutable and cannot be changed or deleted. + * + * @default - Vault Lock can be deleted or changed at any time + */ + readonly changeableFor?: Duration; +} + abstract class BackupVaultBase extends Resource implements IBackupVault { public abstract readonly backupVaultName: string; public abstract readonly backupVaultArn: string; @@ -226,6 +284,7 @@ export class BackupVault extends BackupVaultBase { accessPolicy: Lazy.any({ produce: () => this.accessPolicy.toJSON() }), encryptionKeyArn: props.encryptionKey && props.encryptionKey.keyArn, notifications, + lockConfiguration: renderLockConfiguration(props.lockConfiguration), }); vault.applyRemovalPolicy(props.removalPolicy); @@ -262,3 +321,32 @@ export class BackupVault extends BackupVaultBase { return id.substring(Math.max(id.length - 50, 0), id.length); } } + +function renderLockConfiguration(config?: LockConfiguration): CfnBackupVault.LockConfigurationTypeProperty | undefined { + if (!config) { + return undefined; + } + + if (config.changeableFor && config.changeableFor.toHours() < 72) { + throw new Error(`AWS Backup enforces a 72-hour cooling-off period before Vault Lock takes effect and becomes immutable, got ${config.changeableFor.toHours()} hours`); + } + + if (config.maxRetention) { + if (config.maxRetention.toDays() > 36500) { + throw new Error(`The longest maximum retention period you can specify is 36500 days, got ${config.maxRetention.toDays()} days`); + } + if (config.maxRetention.toDays() <= config.minRetention.toDays()) { + throw new Error(`The maximum retention period (${config.maxRetention.toDays()} days) must be greater than the minimum retention period (${config.minRetention.toDays()} days)`); + } + } + + if (config.minRetention.toHours() < 24) { + throw new Error(`The shortest minimum retention period you can specify is 1 day, got ${config.minRetention.toHours()} hours`); + } + + return { + minRetentionDays: config.minRetention.toDays(), + maxRetentionDays: config.maxRetention?.toDays(), + changeableForDays: config.changeableFor?.toDays(), + }; +} diff --git a/packages/@aws-cdk/aws-backup/test/backup.integ.snapshot/cdk-backup.template.json b/packages/@aws-cdk/aws-backup/test/backup.integ.snapshot/cdk-backup.template.json index a42ff516a5b73..a142eca42bad6 100644 --- a/packages/@aws-cdk/aws-backup/test/backup.integ.snapshot/cdk-backup.template.json +++ b/packages/@aws-cdk/aws-backup/test/backup.integ.snapshot/cdk-backup.template.json @@ -31,7 +31,10 @@ "Vault23237E5B": { "Type": "AWS::Backup::BackupVault", "Properties": { - "BackupVaultName": "cdkbackupVaultC2A6D3CB" + "BackupVaultName": "cdkbackupVaultC2A6D3CB", + "LockConfiguration": { + "MinRetentionDays": 5 + } }, "UpdateReplacePolicy": "Delete", "DeletionPolicy": "Delete" diff --git a/packages/@aws-cdk/aws-backup/test/backup.integ.snapshot/cdk.out b/packages/@aws-cdk/aws-backup/test/backup.integ.snapshot/cdk.out index 90bef2e09ad39..588d7b269d34f 100644 --- a/packages/@aws-cdk/aws-backup/test/backup.integ.snapshot/cdk.out +++ b/packages/@aws-cdk/aws-backup/test/backup.integ.snapshot/cdk.out @@ -1 +1 @@ -{"version":"17.0.0"} \ No newline at end of file +{"version":"20.0.0"} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-backup/test/backup.integ.snapshot/integ.json b/packages/@aws-cdk/aws-backup/test/backup.integ.snapshot/integ.json index 1c562a11594ba..c432d2f16d1fa 100644 --- a/packages/@aws-cdk/aws-backup/test/backup.integ.snapshot/integ.json +++ b/packages/@aws-cdk/aws-backup/test/backup.integ.snapshot/integ.json @@ -1,7 +1,7 @@ { - "version": "18.0.0", + "version": "20.0.0", "testCases": { - "aws-backup/test/integ.backup": { + "integ.backup": { "stacks": [ "cdk-backup" ], diff --git a/packages/@aws-cdk/aws-backup/test/backup.integ.snapshot/manifest.json b/packages/@aws-cdk/aws-backup/test/backup.integ.snapshot/manifest.json index 7c479fbabea2a..84b64e69b40c5 100644 --- a/packages/@aws-cdk/aws-backup/test/backup.integ.snapshot/manifest.json +++ b/packages/@aws-cdk/aws-backup/test/backup.integ.snapshot/manifest.json @@ -1,5 +1,5 @@ { - "version": "17.0.0", + "version": "20.0.0", "artifacts": { "Tree": { "type": "cdk:tree", diff --git a/packages/@aws-cdk/aws-backup/test/backup.integ.snapshot/tree.json b/packages/@aws-cdk/aws-backup/test/backup.integ.snapshot/tree.json index 28c85a58dc3e0..a380843ef8ec6 100644 --- a/packages/@aws-cdk/aws-backup/test/backup.integ.snapshot/tree.json +++ b/packages/@aws-cdk/aws-backup/test/backup.integ.snapshot/tree.json @@ -8,8 +8,8 @@ "id": "Tree", "path": "Tree", "constructInfo": { - "fqn": "@aws-cdk/core.Construct", - "version": "0.0.0" + "fqn": "constructs.Construct", + "version": "10.1.33" } }, "cdk-backup": { @@ -85,7 +85,10 @@ "attributes": { "aws:cdk:cloudformation:type": "AWS::Backup::BackupVault", "aws:cdk:cloudformation:props": { - "backupVaultName": "cdkbackupVaultC2A6D3CB" + "backupVaultName": "cdkbackupVaultC2A6D3CB", + "lockConfiguration": { + "minRetentionDays": 5 + } } }, "constructInfo": { diff --git a/packages/@aws-cdk/aws-backup/test/integ.backup.ts b/packages/@aws-cdk/aws-backup/test/integ.backup.ts index e04d3ba68671a..57de1d51750fc 100644 --- a/packages/@aws-cdk/aws-backup/test/integ.backup.ts +++ b/packages/@aws-cdk/aws-backup/test/integ.backup.ts @@ -1,6 +1,6 @@ import * as dynamodb from '@aws-cdk/aws-dynamodb'; import * as efs from '@aws-cdk/aws-efs'; -import { App, RemovalPolicy, Stack, StackProps } from '@aws-cdk/core'; +import { App, Duration, RemovalPolicy, Stack, StackProps } from '@aws-cdk/core'; import { Construct } from 'constructs'; import * as backup from '../lib'; @@ -21,6 +21,9 @@ class TestStack extends Stack { const vault = new backup.BackupVault(this, 'Vault', { removalPolicy: RemovalPolicy.DESTROY, + lockConfiguration: { + minRetention: Duration.days(5), + }, }); const plan = backup.BackupPlan.dailyWeeklyMonthly5YearRetention(this, 'Plan', vault); diff --git a/packages/@aws-cdk/aws-backup/test/vault.test.ts b/packages/@aws-cdk/aws-backup/test/vault.test.ts index ea63fb16323f2..1030d432f1364 100644 --- a/packages/@aws-cdk/aws-backup/test/vault.test.ts +++ b/packages/@aws-cdk/aws-backup/test/vault.test.ts @@ -2,7 +2,7 @@ import { Template } from '@aws-cdk/assertions'; import * as iam from '@aws-cdk/aws-iam'; import * as kms from '@aws-cdk/aws-kms'; import * as sns from '@aws-cdk/aws-sns'; -import { ArnFormat, Stack } from '@aws-cdk/core'; +import { ArnFormat, Duration, Stack } from '@aws-cdk/core'; import { BackupVault, BackupVaultEvents } from '../lib'; let stack: Stack; @@ -367,3 +367,58 @@ test('throws with too short name', () => { backupVaultName: 'x', })).toThrow(/Expected vault name to match pattern/); }); + +test('with lock configuration', () => { + // WHEN + new BackupVault(stack, 'Vault', { + lockConfiguration: { + minRetention: Duration.days(30), + maxRetention: Duration.days(365), + changeableFor: Duration.days(7), + }, + }); + + // THEN + Template.fromStack(stack).hasResourceProperties('AWS::Backup::BackupVault', { + LockConfiguration: { + ChangeableForDays: 7, + MaxRetentionDays: 365, + MinRetentionDays: 30, + }, + }); +}); + +test('throws with incorrect lock configuration - min retention', () => { + expect(() => new BackupVault(stack, 'Vault', { + lockConfiguration: { + minRetention: Duration.hours(12), + }, + })).toThrow(/The shortest minimum retention period you can specify is 1 day/); +}); + +test('throws with incorrect lock configuration - max retention', () => { + expect(() => new BackupVault(stack, 'Vault', { + lockConfiguration: { + minRetention: Duration.days(7), + maxRetention: Duration.days(40000), + }, + })).toThrow(/The longest maximum retention period you can specify is 36500 days/); +}); + +test('throws with incorrect lock configuration - max and min retention', () => { + expect(() => new BackupVault(stack, 'Vault', { + lockConfiguration: { + minRetention: Duration.days(7), + maxRetention: Duration.days(4), + }, + })).toThrow(/The maximum retention period \(4 days\) must be greater than the minimum retention period \(7 days\)/); +}); + +test('throws with incorrect lock configuration - changeable for', () => { + expect(() => new BackupVault(stack, 'Vault', { + lockConfiguration: { + minRetention: Duration.days(7), + changeableFor: Duration.days(1), + }, + })).toThrow(/AWS Backup enforces a 72-hour cooling-off period before Vault Lock takes effect and becomes immutable/); +}); From 10f8821275c4db0377d11662e1d14dff1dec2f5d Mon Sep 17 00:00:00 2001 From: Joshua Weber <57131123+daschaa@users.noreply.github.com> Date: Tue, 12 Jul 2022 20:23:34 +0200 Subject: [PATCH 24/92] feat(stepfunctions-tasks): support parameters in StepFunctionsInvokeActivity (#21077) Fixes #21020 This PR adds the parameters to the StepFunctionsInvokeActivity class. ---- ### All Submissions: * [x] Have you followed the guidelines in our [Contributing guide?](https://github.com/aws/aws-cdk/blob/main/CONTRIBUTING.md) ### Adding new Unconventional Dependencies: * [ ] This PR adds new unconventional dependencies following the process described [here](https://github.com/aws/aws-cdk/blob/main/CONTRIBUTING.md/#adding-new-unconventional-dependencies) ### New Features * [x] Have you added the new feature to an [integration test](https://github.com/aws/aws-cdk/blob/main/INTEGRATION_TESTS.md)? * [x] Did you use `yarn integ` to deploy the infrastructure and generate the snapshot (i.e. `yarn integ` without `--dry-run`)? *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- .../aws-stepfunctions-tasks/README.md | 19 +++++++++++++++++++ .../lib/stepfunctions/invoke-activity.ts | 10 ++++++++++ .../stepfunctions/integ.invoke-activity.ts | 8 ++++++++ .../aws-stepfunctions-integ.assets.json | 19 +++++++++++++++++++ .../aws-stepfunctions-integ.template.json | 2 +- .../invoke-activity.integ.snapshot/cdk.out | 2 +- .../invoke-activity.integ.snapshot/integ.json | 4 ++-- .../manifest.json | 2 +- .../invoke-activity.integ.snapshot/tree.json | 6 +++--- 9 files changed, 64 insertions(+), 8 deletions(-) create mode 100644 packages/@aws-cdk/aws-stepfunctions-tasks/test/stepfunctions/invoke-activity.integ.snapshot/aws-stepfunctions-integ.assets.json diff --git a/packages/@aws-cdk/aws-stepfunctions-tasks/README.md b/packages/@aws-cdk/aws-stepfunctions-tasks/README.md index ad4c2a365c38b..7dd2cf1ad845e 100644 --- a/packages/@aws-cdk/aws-stepfunctions-tasks/README.md +++ b/packages/@aws-cdk/aws-stepfunctions-tasks/README.md @@ -1404,6 +1404,25 @@ new tasks.StepFunctionsInvokeActivity(this, 'Submit Job', { }); ``` +Use the [Parameters](https://docs.aws.amazon.com/step-functions/latest/dg/input-output-inputpath-params.html#input-output-parameters) field to create a collection of key-value pairs that are passed as input. +The values of each can either be static values that you include in your state machine definition, or selected from either the input or the context object with a path. + +```ts +const submitJobActivity = new sfn.Activity(this, 'SubmitJob'); + +new tasks.StepFunctionsInvokeActivity(this, 'Submit Job', { + activity: submitJobActivity, + parameters: { + comment: 'Selecting what I care about.', + MyDetails: { + size: sfn.JsonPath.stringAt('$.product.details.size'), + exists: sfn.JsonPath.stringAt('$.product.availability'), + StaticValue: 'foo' + }, + }, +}); +``` + ## SQS Step Functions supports [Amazon SQS](https://docs.aws.amazon.com/step-functions/latest/dg/connect-sqs.html) diff --git a/packages/@aws-cdk/aws-stepfunctions-tasks/lib/stepfunctions/invoke-activity.ts b/packages/@aws-cdk/aws-stepfunctions-tasks/lib/stepfunctions/invoke-activity.ts index 0fb6007b8b7bc..a3d123bf04bc7 100644 --- a/packages/@aws-cdk/aws-stepfunctions-tasks/lib/stepfunctions/invoke-activity.ts +++ b/packages/@aws-cdk/aws-stepfunctions-tasks/lib/stepfunctions/invoke-activity.ts @@ -11,6 +11,15 @@ export interface StepFunctionsInvokeActivityProps extends sfn.TaskStateBaseProps * Step Functions Activity to invoke */ readonly activity: sfn.IActivity + + /** + * Parameters pass a collection of key-value pairs, either static values or JSONPath expressions that select from the input. + * + * @see https://docs.aws.amazon.com/step-functions/latest/dg/input-output-inputpath-params.html#input-output-parameters + * + * @default No parameters + */ + readonly parameters?: { [name: string]: any }; } /** @@ -39,6 +48,7 @@ export class StepFunctionsInvokeActivity extends sfn.TaskStateBase { protected _renderTask(): any { return { Resource: this.props.activity.activityArn, + Parameters: this.props.parameters ? sfn.FieldUtils.renderObject(this.props.parameters) : undefined, }; } } diff --git a/packages/@aws-cdk/aws-stepfunctions-tasks/test/stepfunctions/integ.invoke-activity.ts b/packages/@aws-cdk/aws-stepfunctions-tasks/test/stepfunctions/integ.invoke-activity.ts index 918c31d809aaf..c4f3f024dca73 100644 --- a/packages/@aws-cdk/aws-stepfunctions-tasks/test/stepfunctions/integ.invoke-activity.ts +++ b/packages/@aws-cdk/aws-stepfunctions-tasks/test/stepfunctions/integ.invoke-activity.ts @@ -42,6 +42,14 @@ class InvokeActivityStack extends cdk.Stack { const finalStatus = new tasks.StepFunctionsInvokeActivity(this, 'Get Final Job Status', { activity: checkJobActivity, inputPath: '$.guid', + parameters: { + 'input.$': '$', + 'stringArgument': 'inital-task', + 'numberArgument': 123, + 'booleanArgument': true, + 'arrayArgument': ['a', 'b', 'c'], + 'jsonPath': sfn.JsonPath.stringAt('$.status'), + }, }); const chain = sfn.Chain diff --git a/packages/@aws-cdk/aws-stepfunctions-tasks/test/stepfunctions/invoke-activity.integ.snapshot/aws-stepfunctions-integ.assets.json b/packages/@aws-cdk/aws-stepfunctions-tasks/test/stepfunctions/invoke-activity.integ.snapshot/aws-stepfunctions-integ.assets.json new file mode 100644 index 0000000000000..30a5ec55fc9fe --- /dev/null +++ b/packages/@aws-cdk/aws-stepfunctions-tasks/test/stepfunctions/invoke-activity.integ.snapshot/aws-stepfunctions-integ.assets.json @@ -0,0 +1,19 @@ +{ + "version": "20.0.0", + "files": { + "b9edde5b0344ecb8e67455541ca7cae1ee0b7f594b359920852dcb647be6a6f2": { + "source": { + "path": "aws-stepfunctions-integ.template.json", + "packaging": "file" + }, + "destinations": { + "current_account-current_region": { + "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", + "objectKey": "b9edde5b0344ecb8e67455541ca7cae1ee0b7f594b359920852dcb647be6a6f2.json", + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" + } + } + } + }, + "dockerImages": {} +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-stepfunctions-tasks/test/stepfunctions/invoke-activity.integ.snapshot/aws-stepfunctions-integ.template.json b/packages/@aws-cdk/aws-stepfunctions-tasks/test/stepfunctions/invoke-activity.integ.snapshot/aws-stepfunctions-integ.template.json index c62b7ac4097a3..8a0300f605fb4 100644 --- a/packages/@aws-cdk/aws-stepfunctions-tasks/test/stepfunctions/invoke-activity.integ.snapshot/aws-stepfunctions-integ.template.json +++ b/packages/@aws-cdk/aws-stepfunctions-tasks/test/stepfunctions/invoke-activity.integ.snapshot/aws-stepfunctions-integ.template.json @@ -62,7 +62,7 @@ { "Ref": "CheckJob5FFC1D6F" }, - "\"}},\"TimeoutSeconds\":300}" + "\",\"Parameters\":{\"input.$\":\"$\",\"stringArgument\":\"inital-task\",\"numberArgument\":123,\"booleanArgument\":true,\"arrayArgument\":[\"a\",\"b\",\"c\"],\"jsonPath.$\":\"$.status\"}}},\"TimeoutSeconds\":300}" ] ] } diff --git a/packages/@aws-cdk/aws-stepfunctions-tasks/test/stepfunctions/invoke-activity.integ.snapshot/cdk.out b/packages/@aws-cdk/aws-stepfunctions-tasks/test/stepfunctions/invoke-activity.integ.snapshot/cdk.out index 90bef2e09ad39..588d7b269d34f 100644 --- a/packages/@aws-cdk/aws-stepfunctions-tasks/test/stepfunctions/invoke-activity.integ.snapshot/cdk.out +++ b/packages/@aws-cdk/aws-stepfunctions-tasks/test/stepfunctions/invoke-activity.integ.snapshot/cdk.out @@ -1 +1 @@ -{"version":"17.0.0"} \ No newline at end of file +{"version":"20.0.0"} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-stepfunctions-tasks/test/stepfunctions/invoke-activity.integ.snapshot/integ.json b/packages/@aws-cdk/aws-stepfunctions-tasks/test/stepfunctions/invoke-activity.integ.snapshot/integ.json index 79cb5c49f757d..1198a87155e86 100644 --- a/packages/@aws-cdk/aws-stepfunctions-tasks/test/stepfunctions/invoke-activity.integ.snapshot/integ.json +++ b/packages/@aws-cdk/aws-stepfunctions-tasks/test/stepfunctions/invoke-activity.integ.snapshot/integ.json @@ -1,7 +1,7 @@ { - "version": "18.0.0", + "version": "20.0.0", "testCases": { - "aws-stepfunctions-tasks/test/stepfunctions/integ.invoke-activity": { + "stepfunctions/integ.invoke-activity": { "stacks": [ "aws-stepfunctions-integ" ], diff --git a/packages/@aws-cdk/aws-stepfunctions-tasks/test/stepfunctions/invoke-activity.integ.snapshot/manifest.json b/packages/@aws-cdk/aws-stepfunctions-tasks/test/stepfunctions/invoke-activity.integ.snapshot/manifest.json index 4575882e46d5e..c68109ad085de 100644 --- a/packages/@aws-cdk/aws-stepfunctions-tasks/test/stepfunctions/invoke-activity.integ.snapshot/manifest.json +++ b/packages/@aws-cdk/aws-stepfunctions-tasks/test/stepfunctions/invoke-activity.integ.snapshot/manifest.json @@ -1,5 +1,5 @@ { - "version": "17.0.0", + "version": "20.0.0", "artifacts": { "Tree": { "type": "cdk:tree", diff --git a/packages/@aws-cdk/aws-stepfunctions-tasks/test/stepfunctions/invoke-activity.integ.snapshot/tree.json b/packages/@aws-cdk/aws-stepfunctions-tasks/test/stepfunctions/invoke-activity.integ.snapshot/tree.json index 5e6f1538bf15a..346091f46c60c 100644 --- a/packages/@aws-cdk/aws-stepfunctions-tasks/test/stepfunctions/invoke-activity.integ.snapshot/tree.json +++ b/packages/@aws-cdk/aws-stepfunctions-tasks/test/stepfunctions/invoke-activity.integ.snapshot/tree.json @@ -8,8 +8,8 @@ "id": "Tree", "path": "Tree", "constructInfo": { - "fqn": "@aws-cdk/core.Construct", - "version": "0.0.0" + "fqn": "constructs.Construct", + "version": "10.1.33" } }, "aws-stepfunctions-integ": { @@ -187,7 +187,7 @@ { "Ref": "CheckJob5FFC1D6F" }, - "\"}},\"TimeoutSeconds\":300}" + "\",\"Parameters\":{\"input.$\":\"$\",\"stringArgument\":\"inital-task\",\"numberArgument\":123,\"booleanArgument\":true,\"arrayArgument\":[\"a\",\"b\",\"c\"],\"jsonPath.$\":\"$.status\"}}},\"TimeoutSeconds\":300}" ] ] } From b970b8aac4d927db2bbb4e19a0f92553c538aff0 Mon Sep 17 00:00:00 2001 From: Calvin Combs <66279577+comcalvi@users.noreply.github.com> Date: Tue, 12 Jul 2022 13:42:00 -0600 Subject: [PATCH 25/92] Revert "fix(custom-resources): Custom resource provider framework not passing `ResponseURL` to user function" (#21109) This PR did not actually fix the issue with custom resources, it just passed a `ResponseURL: '...'`, which isn't a usable URL. Leaving it as a sanitized string makes it appear as if the `ResponseURL` is available, but it actually isn't. Reverting this PR to not give the false impression that `ResponseURL` is usable. Reverts aws/aws-cdk#21065 --- .../lib/provider-framework/runtime/framework.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/@aws-cdk/custom-resources/lib/provider-framework/runtime/framework.ts b/packages/@aws-cdk/custom-resources/lib/provider-framework/runtime/framework.ts index f9586e032ae5f..97b7c4de61e4b 100644 --- a/packages/@aws-cdk/custom-resources/lib/provider-framework/runtime/framework.ts +++ b/packages/@aws-cdk/custom-resources/lib/provider-framework/runtime/framework.ts @@ -105,7 +105,9 @@ async function invokeUserFunction(functionArnE // automatically by the JavaScript SDK. const resp = await invokeFunction({ FunctionName: functionArn, - Payload: JSON.stringify(sanitizedPayload), + + // Strip 'ResponseURL' -- the downstream CR doesn't need it and can only log it by accident + Payload: JSON.stringify({ ...sanitizedPayload, ResponseURL: undefined }), }); log('user function response:', resp, typeof(resp)); From d38f78c3fc9ba37b3a1033dabe89cd60dfd81b8f Mon Sep 17 00:00:00 2001 From: Cory Hall <43035978+corymhall@users.noreply.github.com> Date: Tue, 12 Jul 2022 16:30:41 -0400 Subject: [PATCH 26/92] fix(integ-runner): test names change depending on the discovery directory (#21093) The name of the test that is stored in the `integ.json` file for legacy test cases changes depending on what directory you run the test from. For example, if you were to run the below command from the root of the project the test name would be `@aws-cdk/aws-lambda/test/integ.assets.file` `yarn integ-runner --directory package/@aws-cdk/aws-lambda test/integ.assets.file.js` If you were to run this command from the `packages/@aws-cdk/aws-lambda` directory then the test name would be `integ.assets.file` `yarn integ integ.assets.file.js` This PR attempts to normalize the test name by using the name of the integration test file, minus the file extension. **Note, this only affects legacy test cases. The new style test cases have a different mechanism to generate a test name. I will create a follow up PR to update all of the `integ.json` files for all of the existing test cases to have the normalized name. ---- ### All Submissions: * [ ] Have you followed the guidelines in our [Contributing guide?](https://github.com/aws/aws-cdk/blob/main/CONTRIBUTING.md) ### Adding new Unconventional Dependencies: * [ ] This PR adds new unconventional dependencies following the process described [here](https://github.com/aws/aws-cdk/blob/main/CONTRIBUTING.md/#adding-new-unconventional-dependencies) ### New Features * [ ] Have you added the new feature to an [integration test](https://github.com/aws/aws-cdk/blob/main/INTEGRATION_TESTS.md)? * [ ] Did you use `yarn integ` to deploy the infrastructure and generate the snapshot (i.e. `yarn integ` without `--dry-run`)? *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- .../@aws-cdk/integ-runner/lib/runner/integration-tests.ts | 8 ++++++++ packages/@aws-cdk/integ-runner/lib/runner/runner-base.ts | 2 +- .../integ-runner/test/runner/snapshot-test-runner.test.ts | 4 ++-- 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/packages/@aws-cdk/integ-runner/lib/runner/integration-tests.ts b/packages/@aws-cdk/integ-runner/lib/runner/integration-tests.ts index ac139187c500a..5259a9d0ff85c 100644 --- a/packages/@aws-cdk/integ-runner/lib/runner/integration-tests.ts +++ b/packages/@aws-cdk/integ-runner/lib/runner/integration-tests.ts @@ -48,6 +48,13 @@ export class IntegTest { */ public readonly absoluteFileName: string; + /** + * The normalized name of the test. This name + * will be the same regardless of what directory the tool + * is run from. + */ + public readonly normalizedTestName: string; + /** * Directory the test is in */ @@ -91,6 +98,7 @@ export class IntegTest { : path.join(path.relative(this.info.discoveryRoot, parsed.dir), parsed.name); const nakedTestName = parsed.name.slice(6); // Leave name without 'integ.' and '.ts' + this.normalizedTestName = parsed.name; this.snapshotDir = path.join(this.directory, `${nakedTestName}.integ.snapshot`); this.temporaryOutputDir = path.join(this.directory, `${CDK_OUTDIR_PREFIX}.${nakedTestName}`); } diff --git a/packages/@aws-cdk/integ-runner/lib/runner/runner-base.ts b/packages/@aws-cdk/integ-runner/lib/runner/runner-base.ts index 052b944da95d4..58ba669ab4d70 100644 --- a/packages/@aws-cdk/integ-runner/lib/runner/runner-base.ts +++ b/packages/@aws-cdk/integ-runner/lib/runner/runner-base.ts @@ -204,7 +204,7 @@ export abstract class IntegRunner { } catch (e) { const testCases = LegacyIntegTestSuite.fromLegacy({ cdk: this.cdk, - testName: this.testName, + testName: this.test.normalizedTestName, integSourceFilePath: this.test.fileName, listOptions: { ...this.defaultArgs, diff --git a/packages/@aws-cdk/integ-runner/test/runner/snapshot-test-runner.test.ts b/packages/@aws-cdk/integ-runner/test/runner/snapshot-test-runner.test.ts index f51116d5272d7..f5247bc390889 100644 --- a/packages/@aws-cdk/integ-runner/test/runner/snapshot-test-runner.test.ts +++ b/packages/@aws-cdk/integ-runner/test/runner/snapshot-test-runner.test.ts @@ -175,7 +175,7 @@ describe('IntegTest runSnapshotTests', () => { // THEN expect(integTest.actualTests()).toEqual(expect.objectContaining({ - 'test-data/xxxxx.integ-test1': { + 'xxxxx.integ-test1': { diffAssets: false, stackUpdateWorkflow: true, stacks: ['stack1'], @@ -197,7 +197,7 @@ describe('IntegTest runSnapshotTests', () => { // THEN expect(integTest.actualTests()).toEqual(expect.objectContaining({ - 'test-data/xxxxx.integ-test2': { + 'xxxxx.integ-test2': { diffAssets: false, stackUpdateWorkflow: true, stacks: ['stackabc'], From 86790b632a997645970f310ac222fc52e3e58a47 Mon Sep 17 00:00:00 2001 From: Markus Spanier Date: Wed, 13 Jul 2022 01:39:21 +0200 Subject: [PATCH 27/92] feat(rds): support rolling instance updates to reduce downtime (#20054) Support defining the instance update behaviour of RDS instances. This allows to switch between bulk (all instances at once) and rolling updates (one instance after another). While bulk updates are faster, they have a higher risk for longer downtimes as all instances might be simultaneously unreachable due to the update. Rolling updates take longer but ensure that all but one instance are not updated and thus downtimes are limited to the (at most two) changes of the primary instance. We keep the current behaviour, namely a bulk update, as default. This implementation follows proposal A by @hixi-hyi in issue #10595. Fixes #10595 --- packages/@aws-cdk/aws-rds/README.md | 23 +- packages/@aws-cdk/aws-rds/lib/cluster.ts | 37 + packages/@aws-cdk/aws-rds/package.json | 1 + .../@aws-cdk/aws-rds/test/cluster.test.ts | 32 +- .../test/integ.rolling-instance-updates.ts | 45 + .../BulkUpdate.template.json | 554 +++++ ...aultTestDeployAssert287956FD.template.json | 1 + .../RollingUpdate.template.json | 556 +++++ .../cdk.out | 1 + .../integ.json | 12 + .../manifest.json | 414 ++++ .../tree.json | 1881 +++++++++++++++++ 12 files changed, 3555 insertions(+), 2 deletions(-) create mode 100644 packages/@aws-cdk/aws-rds/test/integ.rolling-instance-updates.ts create mode 100644 packages/@aws-cdk/aws-rds/test/rolling-instance-updates.integ.snapshot/BulkUpdate.template.json create mode 100644 packages/@aws-cdk/aws-rds/test/rolling-instance-updates.integ.snapshot/InstanceUpdateBehaviorTestsDefaultTestDeployAssert287956FD.template.json create mode 100644 packages/@aws-cdk/aws-rds/test/rolling-instance-updates.integ.snapshot/RollingUpdate.template.json create mode 100644 packages/@aws-cdk/aws-rds/test/rolling-instance-updates.integ.snapshot/cdk.out create mode 100644 packages/@aws-cdk/aws-rds/test/rolling-instance-updates.integ.snapshot/integ.json create mode 100644 packages/@aws-cdk/aws-rds/test/rolling-instance-updates.integ.snapshot/manifest.json create mode 100644 packages/@aws-cdk/aws-rds/test/rolling-instance-updates.integ.snapshot/tree.json diff --git a/packages/@aws-cdk/aws-rds/README.md b/packages/@aws-cdk/aws-rds/README.md index 861c3607abbe3..bcd41b1aaad41 100644 --- a/packages/@aws-cdk/aws-rds/README.md +++ b/packages/@aws-cdk/aws-rds/README.md @@ -63,9 +63,30 @@ new rds.DatabaseClusterFromSnapshot(this, 'Database', { }); ``` +### Updating the database instances in a cluster + +Database cluster instances may be updated in bulk or on a rolling basis. + +An update to all instances in a cluster may cause significant downtime. To reduce the downtime, set the `instanceUpdateBehavior` property in `DatabaseClusterBaseProps` to `InstanceUpdateBehavior.ROLLING`. This adds a dependency between each instance so the update is performed on only one instance at a time. + +Use `InstanceUpdateBehavior.BULK` to update all instances at once. + +```ts +declare const vpc: ec2.Vpc; +const cluster = new rds.DatabaseCluster(this, 'Database', { + engine: rds.DatabaseClusterEngine.auroraMysql({ version: rds.AuroraMysqlEngineVersion.VER_3_01_0 }), + instances: 2, + instanceProps: { + instanceType: ec2.InstanceType.of(ec2.InstanceClass.BURSTABLE3, ec2.InstanceSize.SMALL), + vpc, + }, + instanceUpdateBehaviour: rds.InstanceUpdateBehaviour.ROLLING, // Optional - defaults to rds.InstanceUpdateBehaviour.BULK +}); +``` + ## Starting an instance database -To set up a instance database, define a `DatabaseInstance`. You must +To set up an instance database, define a `DatabaseInstance`. You must always launch a database in a VPC. Use the `vpcSubnets` attribute to control whether your instances will be launched privately or publicly: diff --git a/packages/@aws-cdk/aws-rds/lib/cluster.ts b/packages/@aws-cdk/aws-rds/lib/cluster.ts index 96ded1e61a730..f5f6b29ce871d 100644 --- a/packages/@aws-cdk/aws-rds/lib/cluster.ts +++ b/packages/@aws-cdk/aws-rds/lib/cluster.ts @@ -41,6 +41,13 @@ interface DatabaseClusterBaseProps { */ readonly instanceProps: InstanceProps; + /** + * The ordering of updates for instances + * + * @default InstanceUpdateBehaviour.BULK + */ + readonly instanceUpdateBehaviour?: InstanceUpdateBehaviour; + /** * The number of seconds to set a cluster's target backtrack window to. * This feature is only supported by the Aurora MySQL database engine and @@ -275,6 +282,25 @@ interface DatabaseClusterBaseProps { readonly copyTagsToSnapshot?: boolean; } +/** + * The orchestration of updates of multiple instances + */ +export enum InstanceUpdateBehaviour { + /** + * In a bulk update, all instances of the cluster are updated at the same time. + * This results in a faster update procedure. + * During the update, however, all instances might be unavailable at the same time and thus a downtime might occur. + */ + BULK = 'BULK', + + /** + * In a rolling update, one instance after another is updated. + * This results in at most one instance being unavailable during the update. + * If your cluster consists of more than 1 instance, the downtime periods are limited to the time a primary switch needs. + */ + ROLLING = 'ROLLING' +} + /** * A new or imported clustered database. */ @@ -805,6 +831,7 @@ interface InstanceConfig { */ function createInstances(cluster: DatabaseClusterNew, props: DatabaseClusterBaseProps, subnetGroup: ISubnetGroup): InstanceConfig { const instanceCount = props.instances != null ? props.instances : 2; + const instanceUpdateBehaviour = props.instanceUpdateBehaviour ?? InstanceUpdateBehaviour.BULK; if (Token.isUnresolved(instanceCount)) { throw new Error('The number of instances an RDS Cluster consists of cannot be provided as a deploy-time only value!'); } @@ -852,6 +879,8 @@ function createInstances(cluster: DatabaseClusterNew, props: DatabaseClusterBase ); const instanceParameterGroupConfig = instanceParameterGroup?.bindToInstance({}); + const instances: CfnDBInstance[] = []; + for (let i = 0; i < instanceCount; i++) { const instanceIndex = i + 1; const instanceIdentifier = props.instanceIdentifierBase != null ? `${props.instanceIdentifierBase}${instanceIndex}` : @@ -895,6 +924,14 @@ function createInstances(cluster: DatabaseClusterNew, props: DatabaseClusterBase instanceIdentifiers.push(instance.ref); instanceEndpoints.push(new Endpoint(instance.attrEndpointAddress, portAttribute)); + instances.push(instance); + } + + // Adding dependencies here to ensure that the instances are updated one after the other. + if (instanceUpdateBehaviour === InstanceUpdateBehaviour.ROLLING) { + for (let i = 1; i < instanceCount; i++) { + instances[i].node.addDependency(instances[i-1]); + } } return { instanceEndpoints, instanceIdentifiers }; diff --git a/packages/@aws-cdk/aws-rds/package.json b/packages/@aws-cdk/aws-rds/package.json index d244aa733c5cd..35cf7600f6ad9 100644 --- a/packages/@aws-cdk/aws-rds/package.json +++ b/packages/@aws-cdk/aws-rds/package.json @@ -86,6 +86,7 @@ "@aws-cdk/cdk-build-tools": "0.0.0", "@aws-cdk/custom-resources": "0.0.0", "@aws-cdk/integ-runner": "0.0.0", + "@aws-cdk/integ-tests": "0.0.0", "@aws-cdk/cfn2ts": "0.0.0", "@aws-cdk/cx-api": "0.0.0", "@aws-cdk/pkglint": "0.0.0", diff --git a/packages/@aws-cdk/aws-rds/test/cluster.test.ts b/packages/@aws-cdk/aws-rds/test/cluster.test.ts index a9ccf4b374182..a52c5e0ee353d 100644 --- a/packages/@aws-cdk/aws-rds/test/cluster.test.ts +++ b/packages/@aws-cdk/aws-rds/test/cluster.test.ts @@ -10,7 +10,7 @@ import * as cxapi from '@aws-cdk/cx-api'; import { AuroraEngineVersion, AuroraMysqlEngineVersion, AuroraPostgresEngineVersion, CfnDBCluster, Credentials, DatabaseCluster, DatabaseClusterEngine, DatabaseClusterFromSnapshot, ParameterGroup, PerformanceInsightRetention, SubnetGroup, DatabaseSecret, - DatabaseInstanceEngine, SqlServerEngineVersion, SnapshotCredentials, + DatabaseInstanceEngine, SqlServerEngineVersion, SnapshotCredentials, InstanceUpdateBehaviour, } from '../lib'; describe('cluster', () => { @@ -122,6 +122,36 @@ describe('cluster', () => { }); }); + test('can create a cluster with ROLLING instance update behaviour', () => { + // GIVEN + const stack = testStack(); + const vpc = new ec2.Vpc(stack, 'VPC'); + + // WHEN + new DatabaseCluster(stack, 'Database', { + engine: DatabaseClusterEngine.AURORA, + instances: 5, + instanceProps: { + vpc, + }, + instanceUpdateBehaviour: InstanceUpdateBehaviour.ROLLING, + }); + + // THEN + const instanceResources = Template.fromStack(stack).findResources('AWS::RDS::DBInstance'); + const instances = Object.keys(instanceResources); + const instanceDependencies = Object.values(instanceResources) + .map(properties => (properties.DependsOn as string[]).filter(dependency => instances.includes(dependency))); + // check that there are only required dependencies to form a chain of dependant instances + for (const dependencies of instanceDependencies) { + expect(dependencies.length).toBeLessThanOrEqual(1); + } + // check that all but one instance are a dependency of another instance + const dependantInstances = instanceDependencies.flat(); + expect(dependantInstances).toHaveLength(instances.length - 1); + expect(instances.filter(it => !dependantInstances.includes(it))).toHaveLength(1); + }); + test('can create a cluster with imported vpc and security group', () => { // GIVEN const stack = testStack(); diff --git a/packages/@aws-cdk/aws-rds/test/integ.rolling-instance-updates.ts b/packages/@aws-cdk/aws-rds/test/integ.rolling-instance-updates.ts new file mode 100644 index 0000000000000..733e5023d56d0 --- /dev/null +++ b/packages/@aws-cdk/aws-rds/test/integ.rolling-instance-updates.ts @@ -0,0 +1,45 @@ +import * as ec2 from '@aws-cdk/aws-ec2'; +import * as cdk from '@aws-cdk/core'; +import * as integTests from '@aws-cdk/integ-tests'; +import * as constructs from 'constructs'; +import * as rds from '../lib'; + +interface RollingInstanceUpdateTestStackProps extends cdk.StackProps { + instanceUpdateBehaviour: rds.InstanceUpdateBehaviour; +} + +class RollingInstanceUpdateTestStack extends cdk.Stack { + constructor(scope: constructs.Construct, id: string, props: RollingInstanceUpdateTestStackProps) { + super(scope, id, props); + const vpc = new ec2.Vpc(this, 'Vpc', { + maxAzs: 2, + }); + + new rds.DatabaseCluster(this, 'DatabaseCluster', { + engine: rds.DatabaseClusterEngine.AURORA, + instances: 3, + instanceProps: { + instanceType: ec2.InstanceType.of(ec2.InstanceClass.BURSTABLE3, ec2.InstanceSize.SMALL), + vpc, + }, + removalPolicy: cdk.RemovalPolicy.DESTROY, + instanceUpdateBehaviour: props.instanceUpdateBehaviour, + }); + } +} + + +// Beginning of the test suite +const app = new cdk.App(); +new integTests.IntegTest(app, 'InstanceUpdateBehaviorTests', { + testCases: [ + new RollingInstanceUpdateTestStack(app, 'BulkUpdate', { + instanceUpdateBehaviour: rds.InstanceUpdateBehaviour.BULK, + }), + new RollingInstanceUpdateTestStack(app, 'RollingUpdate', { + instanceUpdateBehaviour: rds.InstanceUpdateBehaviour.ROLLING, + }), + ], +}); + +app.synth(); diff --git a/packages/@aws-cdk/aws-rds/test/rolling-instance-updates.integ.snapshot/BulkUpdate.template.json b/packages/@aws-cdk/aws-rds/test/rolling-instance-updates.integ.snapshot/BulkUpdate.template.json new file mode 100644 index 0000000000000..ab1dd31a6289c --- /dev/null +++ b/packages/@aws-cdk/aws-rds/test/rolling-instance-updates.integ.snapshot/BulkUpdate.template.json @@ -0,0 +1,554 @@ +{ + "Resources": { + "Vpc8378EB38": { + "Type": "AWS::EC2::VPC", + "Properties": { + "CidrBlock": "10.0.0.0/16", + "EnableDnsHostnames": true, + "EnableDnsSupport": true, + "InstanceTenancy": "default", + "Tags": [ + { + "Key": "Name", + "Value": "BulkUpdate/Vpc" + } + ] + } + }, + "VpcPublicSubnet1Subnet5C2D37C4": { + "Type": "AWS::EC2::Subnet", + "Properties": { + "VpcId": { + "Ref": "Vpc8378EB38" + }, + "AvailabilityZone": { + "Fn::Select": [ + 0, + { + "Fn::GetAZs": "" + } + ] + }, + "CidrBlock": "10.0.0.0/18", + "MapPublicIpOnLaunch": true, + "Tags": [ + { + "Key": "aws-cdk:subnet-name", + "Value": "Public" + }, + { + "Key": "aws-cdk:subnet-type", + "Value": "Public" + }, + { + "Key": "Name", + "Value": "BulkUpdate/Vpc/PublicSubnet1" + } + ] + } + }, + "VpcPublicSubnet1RouteTable6C95E38E": { + "Type": "AWS::EC2::RouteTable", + "Properties": { + "VpcId": { + "Ref": "Vpc8378EB38" + }, + "Tags": [ + { + "Key": "Name", + "Value": "BulkUpdate/Vpc/PublicSubnet1" + } + ] + } + }, + "VpcPublicSubnet1RouteTableAssociation97140677": { + "Type": "AWS::EC2::SubnetRouteTableAssociation", + "Properties": { + "RouteTableId": { + "Ref": "VpcPublicSubnet1RouteTable6C95E38E" + }, + "SubnetId": { + "Ref": "VpcPublicSubnet1Subnet5C2D37C4" + } + } + }, + "VpcPublicSubnet1DefaultRoute3DA9E72A": { + "Type": "AWS::EC2::Route", + "Properties": { + "RouteTableId": { + "Ref": "VpcPublicSubnet1RouteTable6C95E38E" + }, + "DestinationCidrBlock": "0.0.0.0/0", + "GatewayId": { + "Ref": "VpcIGWD7BA715C" + } + }, + "DependsOn": [ + "VpcVPCGWBF912B6E" + ] + }, + "VpcPublicSubnet1EIPD7E02669": { + "Type": "AWS::EC2::EIP", + "Properties": { + "Domain": "vpc", + "Tags": [ + { + "Key": "Name", + "Value": "BulkUpdate/Vpc/PublicSubnet1" + } + ] + } + }, + "VpcPublicSubnet1NATGateway4D7517AA": { + "Type": "AWS::EC2::NatGateway", + "Properties": { + "SubnetId": { + "Ref": "VpcPublicSubnet1Subnet5C2D37C4" + }, + "AllocationId": { + "Fn::GetAtt": [ + "VpcPublicSubnet1EIPD7E02669", + "AllocationId" + ] + }, + "Tags": [ + { + "Key": "Name", + "Value": "BulkUpdate/Vpc/PublicSubnet1" + } + ] + } + }, + "VpcPublicSubnet2Subnet691E08A3": { + "Type": "AWS::EC2::Subnet", + "Properties": { + "VpcId": { + "Ref": "Vpc8378EB38" + }, + "AvailabilityZone": { + "Fn::Select": [ + 1, + { + "Fn::GetAZs": "" + } + ] + }, + "CidrBlock": "10.0.64.0/18", + "MapPublicIpOnLaunch": true, + "Tags": [ + { + "Key": "aws-cdk:subnet-name", + "Value": "Public" + }, + { + "Key": "aws-cdk:subnet-type", + "Value": "Public" + }, + { + "Key": "Name", + "Value": "BulkUpdate/Vpc/PublicSubnet2" + } + ] + } + }, + "VpcPublicSubnet2RouteTable94F7E489": { + "Type": "AWS::EC2::RouteTable", + "Properties": { + "VpcId": { + "Ref": "Vpc8378EB38" + }, + "Tags": [ + { + "Key": "Name", + "Value": "BulkUpdate/Vpc/PublicSubnet2" + } + ] + } + }, + "VpcPublicSubnet2RouteTableAssociationDD5762D8": { + "Type": "AWS::EC2::SubnetRouteTableAssociation", + "Properties": { + "RouteTableId": { + "Ref": "VpcPublicSubnet2RouteTable94F7E489" + }, + "SubnetId": { + "Ref": "VpcPublicSubnet2Subnet691E08A3" + } + } + }, + "VpcPublicSubnet2DefaultRoute97F91067": { + "Type": "AWS::EC2::Route", + "Properties": { + "RouteTableId": { + "Ref": "VpcPublicSubnet2RouteTable94F7E489" + }, + "DestinationCidrBlock": "0.0.0.0/0", + "GatewayId": { + "Ref": "VpcIGWD7BA715C" + } + }, + "DependsOn": [ + "VpcVPCGWBF912B6E" + ] + }, + "VpcPublicSubnet2EIP3C605A87": { + "Type": "AWS::EC2::EIP", + "Properties": { + "Domain": "vpc", + "Tags": [ + { + "Key": "Name", + "Value": "BulkUpdate/Vpc/PublicSubnet2" + } + ] + } + }, + "VpcPublicSubnet2NATGateway9182C01D": { + "Type": "AWS::EC2::NatGateway", + "Properties": { + "SubnetId": { + "Ref": "VpcPublicSubnet2Subnet691E08A3" + }, + "AllocationId": { + "Fn::GetAtt": [ + "VpcPublicSubnet2EIP3C605A87", + "AllocationId" + ] + }, + "Tags": [ + { + "Key": "Name", + "Value": "BulkUpdate/Vpc/PublicSubnet2" + } + ] + } + }, + "VpcPrivateSubnet1Subnet536B997A": { + "Type": "AWS::EC2::Subnet", + "Properties": { + "VpcId": { + "Ref": "Vpc8378EB38" + }, + "AvailabilityZone": { + "Fn::Select": [ + 0, + { + "Fn::GetAZs": "" + } + ] + }, + "CidrBlock": "10.0.128.0/18", + "MapPublicIpOnLaunch": false, + "Tags": [ + { + "Key": "aws-cdk:subnet-name", + "Value": "Private" + }, + { + "Key": "aws-cdk:subnet-type", + "Value": "Private" + }, + { + "Key": "Name", + "Value": "BulkUpdate/Vpc/PrivateSubnet1" + } + ] + } + }, + "VpcPrivateSubnet1RouteTableB2C5B500": { + "Type": "AWS::EC2::RouteTable", + "Properties": { + "VpcId": { + "Ref": "Vpc8378EB38" + }, + "Tags": [ + { + "Key": "Name", + "Value": "BulkUpdate/Vpc/PrivateSubnet1" + } + ] + } + }, + "VpcPrivateSubnet1RouteTableAssociation70C59FA6": { + "Type": "AWS::EC2::SubnetRouteTableAssociation", + "Properties": { + "RouteTableId": { + "Ref": "VpcPrivateSubnet1RouteTableB2C5B500" + }, + "SubnetId": { + "Ref": "VpcPrivateSubnet1Subnet536B997A" + } + } + }, + "VpcPrivateSubnet1DefaultRouteBE02A9ED": { + "Type": "AWS::EC2::Route", + "Properties": { + "RouteTableId": { + "Ref": "VpcPrivateSubnet1RouteTableB2C5B500" + }, + "DestinationCidrBlock": "0.0.0.0/0", + "NatGatewayId": { + "Ref": "VpcPublicSubnet1NATGateway4D7517AA" + } + } + }, + "VpcPrivateSubnet2Subnet3788AAA1": { + "Type": "AWS::EC2::Subnet", + "Properties": { + "VpcId": { + "Ref": "Vpc8378EB38" + }, + "AvailabilityZone": { + "Fn::Select": [ + 1, + { + "Fn::GetAZs": "" + } + ] + }, + "CidrBlock": "10.0.192.0/18", + "MapPublicIpOnLaunch": false, + "Tags": [ + { + "Key": "aws-cdk:subnet-name", + "Value": "Private" + }, + { + "Key": "aws-cdk:subnet-type", + "Value": "Private" + }, + { + "Key": "Name", + "Value": "BulkUpdate/Vpc/PrivateSubnet2" + } + ] + } + }, + "VpcPrivateSubnet2RouteTableA678073B": { + "Type": "AWS::EC2::RouteTable", + "Properties": { + "VpcId": { + "Ref": "Vpc8378EB38" + }, + "Tags": [ + { + "Key": "Name", + "Value": "BulkUpdate/Vpc/PrivateSubnet2" + } + ] + } + }, + "VpcPrivateSubnet2RouteTableAssociationA89CAD56": { + "Type": "AWS::EC2::SubnetRouteTableAssociation", + "Properties": { + "RouteTableId": { + "Ref": "VpcPrivateSubnet2RouteTableA678073B" + }, + "SubnetId": { + "Ref": "VpcPrivateSubnet2Subnet3788AAA1" + } + } + }, + "VpcPrivateSubnet2DefaultRoute060D2087": { + "Type": "AWS::EC2::Route", + "Properties": { + "RouteTableId": { + "Ref": "VpcPrivateSubnet2RouteTableA678073B" + }, + "DestinationCidrBlock": "0.0.0.0/0", + "NatGatewayId": { + "Ref": "VpcPublicSubnet2NATGateway9182C01D" + } + } + }, + "VpcIGWD7BA715C": { + "Type": "AWS::EC2::InternetGateway", + "Properties": { + "Tags": [ + { + "Key": "Name", + "Value": "BulkUpdate/Vpc" + } + ] + } + }, + "VpcVPCGWBF912B6E": { + "Type": "AWS::EC2::VPCGatewayAttachment", + "Properties": { + "VpcId": { + "Ref": "Vpc8378EB38" + }, + "InternetGatewayId": { + "Ref": "VpcIGWD7BA715C" + } + } + }, + "DatabaseClusterSubnets7FAE1846": { + "Type": "AWS::RDS::DBSubnetGroup", + "Properties": { + "DBSubnetGroupDescription": "Subnets for DatabaseCluster database", + "SubnetIds": [ + { + "Ref": "VpcPrivateSubnet1Subnet536B997A" + }, + { + "Ref": "VpcPrivateSubnet2Subnet3788AAA1" + } + ] + } + }, + "DatabaseClusterSecurityGroupCBE34284": { + "Type": "AWS::EC2::SecurityGroup", + "Properties": { + "GroupDescription": "RDS security group", + "SecurityGroupEgress": [ + { + "CidrIp": "0.0.0.0/0", + "Description": "Allow all outbound traffic by default", + "IpProtocol": "-1" + } + ], + "VpcId": { + "Ref": "Vpc8378EB38" + } + } + }, + "DatabaseClusterSecret3F333A5B": { + "Type": "AWS::SecretsManager::Secret", + "Properties": { + "Description": { + "Fn::Join": [ + "", + [ + "Generated by the CDK for stack: ", + { + "Ref": "AWS::StackName" + } + ] + ] + }, + "GenerateSecretString": { + "ExcludeCharacters": " %+~`#$&*()|[]{}:;<>?!'/@\"\\", + "GenerateStringKey": "password", + "PasswordLength": 30, + "SecretStringTemplate": "{\"username\":\"admin\"}" + } + }, + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" + }, + "DatabaseClusterSecretAttachmentB8BF2F7B": { + "Type": "AWS::SecretsManager::SecretTargetAttachment", + "Properties": { + "SecretId": { + "Ref": "DatabaseClusterSecret3F333A5B" + }, + "TargetId": { + "Ref": "DatabaseCluster68FC2945" + }, + "TargetType": "AWS::RDS::DBCluster" + } + }, + "DatabaseCluster68FC2945": { + "Type": "AWS::RDS::DBCluster", + "Properties": { + "Engine": "aurora", + "CopyTagsToSnapshot": true, + "DBSubnetGroupName": { + "Ref": "DatabaseClusterSubnets7FAE1846" + }, + "MasterUsername": { + "Fn::Join": [ + "", + [ + "{{resolve:secretsmanager:", + { + "Ref": "DatabaseClusterSecret3F333A5B" + }, + ":SecretString:username::}}" + ] + ] + }, + "MasterUserPassword": { + "Fn::Join": [ + "", + [ + "{{resolve:secretsmanager:", + { + "Ref": "DatabaseClusterSecret3F333A5B" + }, + ":SecretString:password::}}" + ] + ] + }, + "VpcSecurityGroupIds": [ + { + "Fn::GetAtt": [ + "DatabaseClusterSecurityGroupCBE34284", + "GroupId" + ] + } + ] + }, + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" + }, + "DatabaseClusterInstance1C566869D": { + "Type": "AWS::RDS::DBInstance", + "Properties": { + "DBInstanceClass": "db.t3.small", + "DBClusterIdentifier": { + "Ref": "DatabaseCluster68FC2945" + }, + "DBSubnetGroupName": { + "Ref": "DatabaseClusterSubnets7FAE1846" + }, + "Engine": "aurora" + }, + "DependsOn": [ + "VpcPrivateSubnet1DefaultRouteBE02A9ED", + "VpcPrivateSubnet2DefaultRoute060D2087" + ], + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" + }, + "DatabaseClusterInstance252BB9A46": { + "Type": "AWS::RDS::DBInstance", + "Properties": { + "DBInstanceClass": "db.t3.small", + "DBClusterIdentifier": { + "Ref": "DatabaseCluster68FC2945" + }, + "DBSubnetGroupName": { + "Ref": "DatabaseClusterSubnets7FAE1846" + }, + "Engine": "aurora" + }, + "DependsOn": [ + "VpcPrivateSubnet1DefaultRouteBE02A9ED", + "VpcPrivateSubnet2DefaultRoute060D2087" + ], + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" + }, + "DatabaseClusterInstance3EA629143": { + "Type": "AWS::RDS::DBInstance", + "Properties": { + "DBInstanceClass": "db.t3.small", + "DBClusterIdentifier": { + "Ref": "DatabaseCluster68FC2945" + }, + "DBSubnetGroupName": { + "Ref": "DatabaseClusterSubnets7FAE1846" + }, + "Engine": "aurora" + }, + "DependsOn": [ + "VpcPrivateSubnet1DefaultRouteBE02A9ED", + "VpcPrivateSubnet2DefaultRoute060D2087" + ], + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-rds/test/rolling-instance-updates.integ.snapshot/InstanceUpdateBehaviorTestsDefaultTestDeployAssert287956FD.template.json b/packages/@aws-cdk/aws-rds/test/rolling-instance-updates.integ.snapshot/InstanceUpdateBehaviorTestsDefaultTestDeployAssert287956FD.template.json new file mode 100644 index 0000000000000..9e26dfeeb6e64 --- /dev/null +++ b/packages/@aws-cdk/aws-rds/test/rolling-instance-updates.integ.snapshot/InstanceUpdateBehaviorTestsDefaultTestDeployAssert287956FD.template.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-rds/test/rolling-instance-updates.integ.snapshot/RollingUpdate.template.json b/packages/@aws-cdk/aws-rds/test/rolling-instance-updates.integ.snapshot/RollingUpdate.template.json new file mode 100644 index 0000000000000..1ccb708db074e --- /dev/null +++ b/packages/@aws-cdk/aws-rds/test/rolling-instance-updates.integ.snapshot/RollingUpdate.template.json @@ -0,0 +1,556 @@ +{ + "Resources": { + "Vpc8378EB38": { + "Type": "AWS::EC2::VPC", + "Properties": { + "CidrBlock": "10.0.0.0/16", + "EnableDnsHostnames": true, + "EnableDnsSupport": true, + "InstanceTenancy": "default", + "Tags": [ + { + "Key": "Name", + "Value": "RollingUpdate/Vpc" + } + ] + } + }, + "VpcPublicSubnet1Subnet5C2D37C4": { + "Type": "AWS::EC2::Subnet", + "Properties": { + "VpcId": { + "Ref": "Vpc8378EB38" + }, + "AvailabilityZone": { + "Fn::Select": [ + 0, + { + "Fn::GetAZs": "" + } + ] + }, + "CidrBlock": "10.0.0.0/18", + "MapPublicIpOnLaunch": true, + "Tags": [ + { + "Key": "aws-cdk:subnet-name", + "Value": "Public" + }, + { + "Key": "aws-cdk:subnet-type", + "Value": "Public" + }, + { + "Key": "Name", + "Value": "RollingUpdate/Vpc/PublicSubnet1" + } + ] + } + }, + "VpcPublicSubnet1RouteTable6C95E38E": { + "Type": "AWS::EC2::RouteTable", + "Properties": { + "VpcId": { + "Ref": "Vpc8378EB38" + }, + "Tags": [ + { + "Key": "Name", + "Value": "RollingUpdate/Vpc/PublicSubnet1" + } + ] + } + }, + "VpcPublicSubnet1RouteTableAssociation97140677": { + "Type": "AWS::EC2::SubnetRouteTableAssociation", + "Properties": { + "RouteTableId": { + "Ref": "VpcPublicSubnet1RouteTable6C95E38E" + }, + "SubnetId": { + "Ref": "VpcPublicSubnet1Subnet5C2D37C4" + } + } + }, + "VpcPublicSubnet1DefaultRoute3DA9E72A": { + "Type": "AWS::EC2::Route", + "Properties": { + "RouteTableId": { + "Ref": "VpcPublicSubnet1RouteTable6C95E38E" + }, + "DestinationCidrBlock": "0.0.0.0/0", + "GatewayId": { + "Ref": "VpcIGWD7BA715C" + } + }, + "DependsOn": [ + "VpcVPCGWBF912B6E" + ] + }, + "VpcPublicSubnet1EIPD7E02669": { + "Type": "AWS::EC2::EIP", + "Properties": { + "Domain": "vpc", + "Tags": [ + { + "Key": "Name", + "Value": "RollingUpdate/Vpc/PublicSubnet1" + } + ] + } + }, + "VpcPublicSubnet1NATGateway4D7517AA": { + "Type": "AWS::EC2::NatGateway", + "Properties": { + "SubnetId": { + "Ref": "VpcPublicSubnet1Subnet5C2D37C4" + }, + "AllocationId": { + "Fn::GetAtt": [ + "VpcPublicSubnet1EIPD7E02669", + "AllocationId" + ] + }, + "Tags": [ + { + "Key": "Name", + "Value": "RollingUpdate/Vpc/PublicSubnet1" + } + ] + } + }, + "VpcPublicSubnet2Subnet691E08A3": { + "Type": "AWS::EC2::Subnet", + "Properties": { + "VpcId": { + "Ref": "Vpc8378EB38" + }, + "AvailabilityZone": { + "Fn::Select": [ + 1, + { + "Fn::GetAZs": "" + } + ] + }, + "CidrBlock": "10.0.64.0/18", + "MapPublicIpOnLaunch": true, + "Tags": [ + { + "Key": "aws-cdk:subnet-name", + "Value": "Public" + }, + { + "Key": "aws-cdk:subnet-type", + "Value": "Public" + }, + { + "Key": "Name", + "Value": "RollingUpdate/Vpc/PublicSubnet2" + } + ] + } + }, + "VpcPublicSubnet2RouteTable94F7E489": { + "Type": "AWS::EC2::RouteTable", + "Properties": { + "VpcId": { + "Ref": "Vpc8378EB38" + }, + "Tags": [ + { + "Key": "Name", + "Value": "RollingUpdate/Vpc/PublicSubnet2" + } + ] + } + }, + "VpcPublicSubnet2RouteTableAssociationDD5762D8": { + "Type": "AWS::EC2::SubnetRouteTableAssociation", + "Properties": { + "RouteTableId": { + "Ref": "VpcPublicSubnet2RouteTable94F7E489" + }, + "SubnetId": { + "Ref": "VpcPublicSubnet2Subnet691E08A3" + } + } + }, + "VpcPublicSubnet2DefaultRoute97F91067": { + "Type": "AWS::EC2::Route", + "Properties": { + "RouteTableId": { + "Ref": "VpcPublicSubnet2RouteTable94F7E489" + }, + "DestinationCidrBlock": "0.0.0.0/0", + "GatewayId": { + "Ref": "VpcIGWD7BA715C" + } + }, + "DependsOn": [ + "VpcVPCGWBF912B6E" + ] + }, + "VpcPublicSubnet2EIP3C605A87": { + "Type": "AWS::EC2::EIP", + "Properties": { + "Domain": "vpc", + "Tags": [ + { + "Key": "Name", + "Value": "RollingUpdate/Vpc/PublicSubnet2" + } + ] + } + }, + "VpcPublicSubnet2NATGateway9182C01D": { + "Type": "AWS::EC2::NatGateway", + "Properties": { + "SubnetId": { + "Ref": "VpcPublicSubnet2Subnet691E08A3" + }, + "AllocationId": { + "Fn::GetAtt": [ + "VpcPublicSubnet2EIP3C605A87", + "AllocationId" + ] + }, + "Tags": [ + { + "Key": "Name", + "Value": "RollingUpdate/Vpc/PublicSubnet2" + } + ] + } + }, + "VpcPrivateSubnet1Subnet536B997A": { + "Type": "AWS::EC2::Subnet", + "Properties": { + "VpcId": { + "Ref": "Vpc8378EB38" + }, + "AvailabilityZone": { + "Fn::Select": [ + 0, + { + "Fn::GetAZs": "" + } + ] + }, + "CidrBlock": "10.0.128.0/18", + "MapPublicIpOnLaunch": false, + "Tags": [ + { + "Key": "aws-cdk:subnet-name", + "Value": "Private" + }, + { + "Key": "aws-cdk:subnet-type", + "Value": "Private" + }, + { + "Key": "Name", + "Value": "RollingUpdate/Vpc/PrivateSubnet1" + } + ] + } + }, + "VpcPrivateSubnet1RouteTableB2C5B500": { + "Type": "AWS::EC2::RouteTable", + "Properties": { + "VpcId": { + "Ref": "Vpc8378EB38" + }, + "Tags": [ + { + "Key": "Name", + "Value": "RollingUpdate/Vpc/PrivateSubnet1" + } + ] + } + }, + "VpcPrivateSubnet1RouteTableAssociation70C59FA6": { + "Type": "AWS::EC2::SubnetRouteTableAssociation", + "Properties": { + "RouteTableId": { + "Ref": "VpcPrivateSubnet1RouteTableB2C5B500" + }, + "SubnetId": { + "Ref": "VpcPrivateSubnet1Subnet536B997A" + } + } + }, + "VpcPrivateSubnet1DefaultRouteBE02A9ED": { + "Type": "AWS::EC2::Route", + "Properties": { + "RouteTableId": { + "Ref": "VpcPrivateSubnet1RouteTableB2C5B500" + }, + "DestinationCidrBlock": "0.0.0.0/0", + "NatGatewayId": { + "Ref": "VpcPublicSubnet1NATGateway4D7517AA" + } + } + }, + "VpcPrivateSubnet2Subnet3788AAA1": { + "Type": "AWS::EC2::Subnet", + "Properties": { + "VpcId": { + "Ref": "Vpc8378EB38" + }, + "AvailabilityZone": { + "Fn::Select": [ + 1, + { + "Fn::GetAZs": "" + } + ] + }, + "CidrBlock": "10.0.192.0/18", + "MapPublicIpOnLaunch": false, + "Tags": [ + { + "Key": "aws-cdk:subnet-name", + "Value": "Private" + }, + { + "Key": "aws-cdk:subnet-type", + "Value": "Private" + }, + { + "Key": "Name", + "Value": "RollingUpdate/Vpc/PrivateSubnet2" + } + ] + } + }, + "VpcPrivateSubnet2RouteTableA678073B": { + "Type": "AWS::EC2::RouteTable", + "Properties": { + "VpcId": { + "Ref": "Vpc8378EB38" + }, + "Tags": [ + { + "Key": "Name", + "Value": "RollingUpdate/Vpc/PrivateSubnet2" + } + ] + } + }, + "VpcPrivateSubnet2RouteTableAssociationA89CAD56": { + "Type": "AWS::EC2::SubnetRouteTableAssociation", + "Properties": { + "RouteTableId": { + "Ref": "VpcPrivateSubnet2RouteTableA678073B" + }, + "SubnetId": { + "Ref": "VpcPrivateSubnet2Subnet3788AAA1" + } + } + }, + "VpcPrivateSubnet2DefaultRoute060D2087": { + "Type": "AWS::EC2::Route", + "Properties": { + "RouteTableId": { + "Ref": "VpcPrivateSubnet2RouteTableA678073B" + }, + "DestinationCidrBlock": "0.0.0.0/0", + "NatGatewayId": { + "Ref": "VpcPublicSubnet2NATGateway9182C01D" + } + } + }, + "VpcIGWD7BA715C": { + "Type": "AWS::EC2::InternetGateway", + "Properties": { + "Tags": [ + { + "Key": "Name", + "Value": "RollingUpdate/Vpc" + } + ] + } + }, + "VpcVPCGWBF912B6E": { + "Type": "AWS::EC2::VPCGatewayAttachment", + "Properties": { + "VpcId": { + "Ref": "Vpc8378EB38" + }, + "InternetGatewayId": { + "Ref": "VpcIGWD7BA715C" + } + } + }, + "DatabaseClusterSubnets7FAE1846": { + "Type": "AWS::RDS::DBSubnetGroup", + "Properties": { + "DBSubnetGroupDescription": "Subnets for DatabaseCluster database", + "SubnetIds": [ + { + "Ref": "VpcPrivateSubnet1Subnet536B997A" + }, + { + "Ref": "VpcPrivateSubnet2Subnet3788AAA1" + } + ] + } + }, + "DatabaseClusterSecurityGroupCBE34284": { + "Type": "AWS::EC2::SecurityGroup", + "Properties": { + "GroupDescription": "RDS security group", + "SecurityGroupEgress": [ + { + "CidrIp": "0.0.0.0/0", + "Description": "Allow all outbound traffic by default", + "IpProtocol": "-1" + } + ], + "VpcId": { + "Ref": "Vpc8378EB38" + } + } + }, + "DatabaseClusterSecret3F333A5B": { + "Type": "AWS::SecretsManager::Secret", + "Properties": { + "Description": { + "Fn::Join": [ + "", + [ + "Generated by the CDK for stack: ", + { + "Ref": "AWS::StackName" + } + ] + ] + }, + "GenerateSecretString": { + "ExcludeCharacters": " %+~`#$&*()|[]{}:;<>?!'/@\"\\", + "GenerateStringKey": "password", + "PasswordLength": 30, + "SecretStringTemplate": "{\"username\":\"admin\"}" + } + }, + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" + }, + "DatabaseClusterSecretAttachmentB8BF2F7B": { + "Type": "AWS::SecretsManager::SecretTargetAttachment", + "Properties": { + "SecretId": { + "Ref": "DatabaseClusterSecret3F333A5B" + }, + "TargetId": { + "Ref": "DatabaseCluster68FC2945" + }, + "TargetType": "AWS::RDS::DBCluster" + } + }, + "DatabaseCluster68FC2945": { + "Type": "AWS::RDS::DBCluster", + "Properties": { + "Engine": "aurora", + "CopyTagsToSnapshot": true, + "DBSubnetGroupName": { + "Ref": "DatabaseClusterSubnets7FAE1846" + }, + "MasterUsername": { + "Fn::Join": [ + "", + [ + "{{resolve:secretsmanager:", + { + "Ref": "DatabaseClusterSecret3F333A5B" + }, + ":SecretString:username::}}" + ] + ] + }, + "MasterUserPassword": { + "Fn::Join": [ + "", + [ + "{{resolve:secretsmanager:", + { + "Ref": "DatabaseClusterSecret3F333A5B" + }, + ":SecretString:password::}}" + ] + ] + }, + "VpcSecurityGroupIds": [ + { + "Fn::GetAtt": [ + "DatabaseClusterSecurityGroupCBE34284", + "GroupId" + ] + } + ] + }, + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" + }, + "DatabaseClusterInstance1C566869D": { + "Type": "AWS::RDS::DBInstance", + "Properties": { + "DBInstanceClass": "db.t3.small", + "DBClusterIdentifier": { + "Ref": "DatabaseCluster68FC2945" + }, + "DBSubnetGroupName": { + "Ref": "DatabaseClusterSubnets7FAE1846" + }, + "Engine": "aurora" + }, + "DependsOn": [ + "VpcPrivateSubnet1DefaultRouteBE02A9ED", + "VpcPrivateSubnet2DefaultRoute060D2087" + ], + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" + }, + "DatabaseClusterInstance252BB9A46": { + "Type": "AWS::RDS::DBInstance", + "Properties": { + "DBInstanceClass": "db.t3.small", + "DBClusterIdentifier": { + "Ref": "DatabaseCluster68FC2945" + }, + "DBSubnetGroupName": { + "Ref": "DatabaseClusterSubnets7FAE1846" + }, + "Engine": "aurora" + }, + "DependsOn": [ + "DatabaseClusterInstance1C566869D", + "VpcPrivateSubnet1DefaultRouteBE02A9ED", + "VpcPrivateSubnet2DefaultRoute060D2087" + ], + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" + }, + "DatabaseClusterInstance3EA629143": { + "Type": "AWS::RDS::DBInstance", + "Properties": { + "DBInstanceClass": "db.t3.small", + "DBClusterIdentifier": { + "Ref": "DatabaseCluster68FC2945" + }, + "DBSubnetGroupName": { + "Ref": "DatabaseClusterSubnets7FAE1846" + }, + "Engine": "aurora" + }, + "DependsOn": [ + "DatabaseClusterInstance252BB9A46", + "VpcPrivateSubnet1DefaultRouteBE02A9ED", + "VpcPrivateSubnet2DefaultRoute060D2087" + ], + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-rds/test/rolling-instance-updates.integ.snapshot/cdk.out b/packages/@aws-cdk/aws-rds/test/rolling-instance-updates.integ.snapshot/cdk.out new file mode 100644 index 0000000000000..588d7b269d34f --- /dev/null +++ b/packages/@aws-cdk/aws-rds/test/rolling-instance-updates.integ.snapshot/cdk.out @@ -0,0 +1 @@ +{"version":"20.0.0"} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-rds/test/rolling-instance-updates.integ.snapshot/integ.json b/packages/@aws-cdk/aws-rds/test/rolling-instance-updates.integ.snapshot/integ.json new file mode 100644 index 0000000000000..29249d2b0f31e --- /dev/null +++ b/packages/@aws-cdk/aws-rds/test/rolling-instance-updates.integ.snapshot/integ.json @@ -0,0 +1,12 @@ +{ + "version": "20.0.0", + "testCases": { + "InstanceUpdateBehaviorTests/DefaultTest": { + "stacks": [ + "BulkUpdate", + "RollingUpdate" + ], + "assertionStack": "InstanceUpdateBehaviorTestsDefaultTestDeployAssert287956FD" + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-rds/test/rolling-instance-updates.integ.snapshot/manifest.json b/packages/@aws-cdk/aws-rds/test/rolling-instance-updates.integ.snapshot/manifest.json new file mode 100644 index 0000000000000..f04d94dc78966 --- /dev/null +++ b/packages/@aws-cdk/aws-rds/test/rolling-instance-updates.integ.snapshot/manifest.json @@ -0,0 +1,414 @@ +{ + "version": "20.0.0", + "artifacts": { + "Tree": { + "type": "cdk:tree", + "properties": { + "file": "tree.json" + } + }, + "BulkUpdate": { + "type": "aws:cloudformation:stack", + "environment": "aws://unknown-account/unknown-region", + "properties": { + "templateFile": "BulkUpdate.template.json", + "validateOnSynth": false + }, + "metadata": { + "/BulkUpdate/Vpc/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "Vpc8378EB38" + } + ], + "/BulkUpdate/Vpc/PublicSubnet1/Subnet": [ + { + "type": "aws:cdk:logicalId", + "data": "VpcPublicSubnet1Subnet5C2D37C4" + } + ], + "/BulkUpdate/Vpc/PublicSubnet1/RouteTable": [ + { + "type": "aws:cdk:logicalId", + "data": "VpcPublicSubnet1RouteTable6C95E38E" + } + ], + "/BulkUpdate/Vpc/PublicSubnet1/RouteTableAssociation": [ + { + "type": "aws:cdk:logicalId", + "data": "VpcPublicSubnet1RouteTableAssociation97140677" + } + ], + "/BulkUpdate/Vpc/PublicSubnet1/DefaultRoute": [ + { + "type": "aws:cdk:logicalId", + "data": "VpcPublicSubnet1DefaultRoute3DA9E72A" + } + ], + "/BulkUpdate/Vpc/PublicSubnet1/EIP": [ + { + "type": "aws:cdk:logicalId", + "data": "VpcPublicSubnet1EIPD7E02669" + } + ], + "/BulkUpdate/Vpc/PublicSubnet1/NATGateway": [ + { + "type": "aws:cdk:logicalId", + "data": "VpcPublicSubnet1NATGateway4D7517AA" + } + ], + "/BulkUpdate/Vpc/PublicSubnet2/Subnet": [ + { + "type": "aws:cdk:logicalId", + "data": "VpcPublicSubnet2Subnet691E08A3" + } + ], + "/BulkUpdate/Vpc/PublicSubnet2/RouteTable": [ + { + "type": "aws:cdk:logicalId", + "data": "VpcPublicSubnet2RouteTable94F7E489" + } + ], + "/BulkUpdate/Vpc/PublicSubnet2/RouteTableAssociation": [ + { + "type": "aws:cdk:logicalId", + "data": "VpcPublicSubnet2RouteTableAssociationDD5762D8" + } + ], + "/BulkUpdate/Vpc/PublicSubnet2/DefaultRoute": [ + { + "type": "aws:cdk:logicalId", + "data": "VpcPublicSubnet2DefaultRoute97F91067" + } + ], + "/BulkUpdate/Vpc/PublicSubnet2/EIP": [ + { + "type": "aws:cdk:logicalId", + "data": "VpcPublicSubnet2EIP3C605A87" + } + ], + "/BulkUpdate/Vpc/PublicSubnet2/NATGateway": [ + { + "type": "aws:cdk:logicalId", + "data": "VpcPublicSubnet2NATGateway9182C01D" + } + ], + "/BulkUpdate/Vpc/PrivateSubnet1/Subnet": [ + { + "type": "aws:cdk:logicalId", + "data": "VpcPrivateSubnet1Subnet536B997A" + } + ], + "/BulkUpdate/Vpc/PrivateSubnet1/RouteTable": [ + { + "type": "aws:cdk:logicalId", + "data": "VpcPrivateSubnet1RouteTableB2C5B500" + } + ], + "/BulkUpdate/Vpc/PrivateSubnet1/RouteTableAssociation": [ + { + "type": "aws:cdk:logicalId", + "data": "VpcPrivateSubnet1RouteTableAssociation70C59FA6" + } + ], + "/BulkUpdate/Vpc/PrivateSubnet1/DefaultRoute": [ + { + "type": "aws:cdk:logicalId", + "data": "VpcPrivateSubnet1DefaultRouteBE02A9ED" + } + ], + "/BulkUpdate/Vpc/PrivateSubnet2/Subnet": [ + { + "type": "aws:cdk:logicalId", + "data": "VpcPrivateSubnet2Subnet3788AAA1" + } + ], + "/BulkUpdate/Vpc/PrivateSubnet2/RouteTable": [ + { + "type": "aws:cdk:logicalId", + "data": "VpcPrivateSubnet2RouteTableA678073B" + } + ], + "/BulkUpdate/Vpc/PrivateSubnet2/RouteTableAssociation": [ + { + "type": "aws:cdk:logicalId", + "data": "VpcPrivateSubnet2RouteTableAssociationA89CAD56" + } + ], + "/BulkUpdate/Vpc/PrivateSubnet2/DefaultRoute": [ + { + "type": "aws:cdk:logicalId", + "data": "VpcPrivateSubnet2DefaultRoute060D2087" + } + ], + "/BulkUpdate/Vpc/IGW": [ + { + "type": "aws:cdk:logicalId", + "data": "VpcIGWD7BA715C" + } + ], + "/BulkUpdate/Vpc/VPCGW": [ + { + "type": "aws:cdk:logicalId", + "data": "VpcVPCGWBF912B6E" + } + ], + "/BulkUpdate/DatabaseCluster/Subnets/Default": [ + { + "type": "aws:cdk:logicalId", + "data": "DatabaseClusterSubnets7FAE1846" + } + ], + "/BulkUpdate/DatabaseCluster/SecurityGroup/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "DatabaseClusterSecurityGroupCBE34284" + } + ], + "/BulkUpdate/DatabaseCluster/Secret/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "DatabaseClusterSecret3F333A5B" + } + ], + "/BulkUpdate/DatabaseCluster/Secret/Attachment/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "DatabaseClusterSecretAttachmentB8BF2F7B" + } + ], + "/BulkUpdate/DatabaseCluster/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "DatabaseCluster68FC2945" + } + ], + "/BulkUpdate/DatabaseCluster/Instance1": [ + { + "type": "aws:cdk:logicalId", + "data": "DatabaseClusterInstance1C566869D" + } + ], + "/BulkUpdate/DatabaseCluster/Instance2": [ + { + "type": "aws:cdk:logicalId", + "data": "DatabaseClusterInstance252BB9A46" + } + ], + "/BulkUpdate/DatabaseCluster/Instance3": [ + { + "type": "aws:cdk:logicalId", + "data": "DatabaseClusterInstance3EA629143" + } + ] + }, + "displayName": "BulkUpdate" + }, + "RollingUpdate": { + "type": "aws:cloudformation:stack", + "environment": "aws://unknown-account/unknown-region", + "properties": { + "templateFile": "RollingUpdate.template.json", + "validateOnSynth": false + }, + "metadata": { + "/RollingUpdate/Vpc/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "Vpc8378EB38" + } + ], + "/RollingUpdate/Vpc/PublicSubnet1/Subnet": [ + { + "type": "aws:cdk:logicalId", + "data": "VpcPublicSubnet1Subnet5C2D37C4" + } + ], + "/RollingUpdate/Vpc/PublicSubnet1/RouteTable": [ + { + "type": "aws:cdk:logicalId", + "data": "VpcPublicSubnet1RouteTable6C95E38E" + } + ], + "/RollingUpdate/Vpc/PublicSubnet1/RouteTableAssociation": [ + { + "type": "aws:cdk:logicalId", + "data": "VpcPublicSubnet1RouteTableAssociation97140677" + } + ], + "/RollingUpdate/Vpc/PublicSubnet1/DefaultRoute": [ + { + "type": "aws:cdk:logicalId", + "data": "VpcPublicSubnet1DefaultRoute3DA9E72A" + } + ], + "/RollingUpdate/Vpc/PublicSubnet1/EIP": [ + { + "type": "aws:cdk:logicalId", + "data": "VpcPublicSubnet1EIPD7E02669" + } + ], + "/RollingUpdate/Vpc/PublicSubnet1/NATGateway": [ + { + "type": "aws:cdk:logicalId", + "data": "VpcPublicSubnet1NATGateway4D7517AA" + } + ], + "/RollingUpdate/Vpc/PublicSubnet2/Subnet": [ + { + "type": "aws:cdk:logicalId", + "data": "VpcPublicSubnet2Subnet691E08A3" + } + ], + "/RollingUpdate/Vpc/PublicSubnet2/RouteTable": [ + { + "type": "aws:cdk:logicalId", + "data": "VpcPublicSubnet2RouteTable94F7E489" + } + ], + "/RollingUpdate/Vpc/PublicSubnet2/RouteTableAssociation": [ + { + "type": "aws:cdk:logicalId", + "data": "VpcPublicSubnet2RouteTableAssociationDD5762D8" + } + ], + "/RollingUpdate/Vpc/PublicSubnet2/DefaultRoute": [ + { + "type": "aws:cdk:logicalId", + "data": "VpcPublicSubnet2DefaultRoute97F91067" + } + ], + "/RollingUpdate/Vpc/PublicSubnet2/EIP": [ + { + "type": "aws:cdk:logicalId", + "data": "VpcPublicSubnet2EIP3C605A87" + } + ], + "/RollingUpdate/Vpc/PublicSubnet2/NATGateway": [ + { + "type": "aws:cdk:logicalId", + "data": "VpcPublicSubnet2NATGateway9182C01D" + } + ], + "/RollingUpdate/Vpc/PrivateSubnet1/Subnet": [ + { + "type": "aws:cdk:logicalId", + "data": "VpcPrivateSubnet1Subnet536B997A" + } + ], + "/RollingUpdate/Vpc/PrivateSubnet1/RouteTable": [ + { + "type": "aws:cdk:logicalId", + "data": "VpcPrivateSubnet1RouteTableB2C5B500" + } + ], + "/RollingUpdate/Vpc/PrivateSubnet1/RouteTableAssociation": [ + { + "type": "aws:cdk:logicalId", + "data": "VpcPrivateSubnet1RouteTableAssociation70C59FA6" + } + ], + "/RollingUpdate/Vpc/PrivateSubnet1/DefaultRoute": [ + { + "type": "aws:cdk:logicalId", + "data": "VpcPrivateSubnet1DefaultRouteBE02A9ED" + } + ], + "/RollingUpdate/Vpc/PrivateSubnet2/Subnet": [ + { + "type": "aws:cdk:logicalId", + "data": "VpcPrivateSubnet2Subnet3788AAA1" + } + ], + "/RollingUpdate/Vpc/PrivateSubnet2/RouteTable": [ + { + "type": "aws:cdk:logicalId", + "data": "VpcPrivateSubnet2RouteTableA678073B" + } + ], + "/RollingUpdate/Vpc/PrivateSubnet2/RouteTableAssociation": [ + { + "type": "aws:cdk:logicalId", + "data": "VpcPrivateSubnet2RouteTableAssociationA89CAD56" + } + ], + "/RollingUpdate/Vpc/PrivateSubnet2/DefaultRoute": [ + { + "type": "aws:cdk:logicalId", + "data": "VpcPrivateSubnet2DefaultRoute060D2087" + } + ], + "/RollingUpdate/Vpc/IGW": [ + { + "type": "aws:cdk:logicalId", + "data": "VpcIGWD7BA715C" + } + ], + "/RollingUpdate/Vpc/VPCGW": [ + { + "type": "aws:cdk:logicalId", + "data": "VpcVPCGWBF912B6E" + } + ], + "/RollingUpdate/DatabaseCluster/Subnets/Default": [ + { + "type": "aws:cdk:logicalId", + "data": "DatabaseClusterSubnets7FAE1846" + } + ], + "/RollingUpdate/DatabaseCluster/SecurityGroup/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "DatabaseClusterSecurityGroupCBE34284" + } + ], + "/RollingUpdate/DatabaseCluster/Secret/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "DatabaseClusterSecret3F333A5B" + } + ], + "/RollingUpdate/DatabaseCluster/Secret/Attachment/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "DatabaseClusterSecretAttachmentB8BF2F7B" + } + ], + "/RollingUpdate/DatabaseCluster/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "DatabaseCluster68FC2945" + } + ], + "/RollingUpdate/DatabaseCluster/Instance1": [ + { + "type": "aws:cdk:logicalId", + "data": "DatabaseClusterInstance1C566869D" + } + ], + "/RollingUpdate/DatabaseCluster/Instance2": [ + { + "type": "aws:cdk:logicalId", + "data": "DatabaseClusterInstance252BB9A46" + } + ], + "/RollingUpdate/DatabaseCluster/Instance3": [ + { + "type": "aws:cdk:logicalId", + "data": "DatabaseClusterInstance3EA629143" + } + ] + }, + "displayName": "RollingUpdate" + }, + "InstanceUpdateBehaviorTestsDefaultTestDeployAssert287956FD": { + "type": "aws:cloudformation:stack", + "environment": "aws://unknown-account/unknown-region", + "properties": { + "templateFile": "InstanceUpdateBehaviorTestsDefaultTestDeployAssert287956FD.template.json", + "validateOnSynth": false + }, + "displayName": "InstanceUpdateBehaviorTests/DefaultTest/DeployAssert" + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-rds/test/rolling-instance-updates.integ.snapshot/tree.json b/packages/@aws-cdk/aws-rds/test/rolling-instance-updates.integ.snapshot/tree.json new file mode 100644 index 0000000000000..468c2cefc448e --- /dev/null +++ b/packages/@aws-cdk/aws-rds/test/rolling-instance-updates.integ.snapshot/tree.json @@ -0,0 +1,1881 @@ +{ + "version": "tree-0.1", + "tree": { + "id": "App", + "path": "", + "children": { + "Tree": { + "id": "Tree", + "path": "Tree", + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.1.33" + } + }, + "BulkUpdate": { + "id": "BulkUpdate", + "path": "BulkUpdate", + "children": { + "Vpc": { + "id": "Vpc", + "path": "BulkUpdate/Vpc", + "children": { + "Resource": { + "id": "Resource", + "path": "BulkUpdate/Vpc/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::VPC", + "aws:cdk:cloudformation:props": { + "cidrBlock": "10.0.0.0/16", + "enableDnsHostnames": true, + "enableDnsSupport": true, + "instanceTenancy": "default", + "tags": [ + { + "key": "Name", + "value": "BulkUpdate/Vpc" + } + ] + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-ec2.CfnVPC", + "version": "0.0.0" + } + }, + "PublicSubnet1": { + "id": "PublicSubnet1", + "path": "BulkUpdate/Vpc/PublicSubnet1", + "children": { + "Subnet": { + "id": "Subnet", + "path": "BulkUpdate/Vpc/PublicSubnet1/Subnet", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::Subnet", + "aws:cdk:cloudformation:props": { + "vpcId": { + "Ref": "Vpc8378EB38" + }, + "availabilityZone": { + "Fn::Select": [ + 0, + { + "Fn::GetAZs": "" + } + ] + }, + "cidrBlock": "10.0.0.0/18", + "mapPublicIpOnLaunch": true, + "tags": [ + { + "key": "aws-cdk:subnet-name", + "value": "Public" + }, + { + "key": "aws-cdk:subnet-type", + "value": "Public" + }, + { + "key": "Name", + "value": "BulkUpdate/Vpc/PublicSubnet1" + } + ] + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-ec2.CfnSubnet", + "version": "0.0.0" + } + }, + "Acl": { + "id": "Acl", + "path": "BulkUpdate/Vpc/PublicSubnet1/Acl", + "constructInfo": { + "fqn": "@aws-cdk/core.Resource", + "version": "0.0.0" + } + }, + "RouteTable": { + "id": "RouteTable", + "path": "BulkUpdate/Vpc/PublicSubnet1/RouteTable", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::RouteTable", + "aws:cdk:cloudformation:props": { + "vpcId": { + "Ref": "Vpc8378EB38" + }, + "tags": [ + { + "key": "Name", + "value": "BulkUpdate/Vpc/PublicSubnet1" + } + ] + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-ec2.CfnRouteTable", + "version": "0.0.0" + } + }, + "RouteTableAssociation": { + "id": "RouteTableAssociation", + "path": "BulkUpdate/Vpc/PublicSubnet1/RouteTableAssociation", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::SubnetRouteTableAssociation", + "aws:cdk:cloudformation:props": { + "routeTableId": { + "Ref": "VpcPublicSubnet1RouteTable6C95E38E" + }, + "subnetId": { + "Ref": "VpcPublicSubnet1Subnet5C2D37C4" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-ec2.CfnSubnetRouteTableAssociation", + "version": "0.0.0" + } + }, + "DefaultRoute": { + "id": "DefaultRoute", + "path": "BulkUpdate/Vpc/PublicSubnet1/DefaultRoute", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::Route", + "aws:cdk:cloudformation:props": { + "routeTableId": { + "Ref": "VpcPublicSubnet1RouteTable6C95E38E" + }, + "destinationCidrBlock": "0.0.0.0/0", + "gatewayId": { + "Ref": "VpcIGWD7BA715C" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-ec2.CfnRoute", + "version": "0.0.0" + } + }, + "EIP": { + "id": "EIP", + "path": "BulkUpdate/Vpc/PublicSubnet1/EIP", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::EIP", + "aws:cdk:cloudformation:props": { + "domain": "vpc", + "tags": [ + { + "key": "Name", + "value": "BulkUpdate/Vpc/PublicSubnet1" + } + ] + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-ec2.CfnEIP", + "version": "0.0.0" + } + }, + "NATGateway": { + "id": "NATGateway", + "path": "BulkUpdate/Vpc/PublicSubnet1/NATGateway", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::NatGateway", + "aws:cdk:cloudformation:props": { + "subnetId": { + "Ref": "VpcPublicSubnet1Subnet5C2D37C4" + }, + "allocationId": { + "Fn::GetAtt": [ + "VpcPublicSubnet1EIPD7E02669", + "AllocationId" + ] + }, + "tags": [ + { + "key": "Name", + "value": "BulkUpdate/Vpc/PublicSubnet1" + } + ] + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-ec2.CfnNatGateway", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-ec2.PublicSubnet", + "version": "0.0.0" + } + }, + "PublicSubnet2": { + "id": "PublicSubnet2", + "path": "BulkUpdate/Vpc/PublicSubnet2", + "children": { + "Subnet": { + "id": "Subnet", + "path": "BulkUpdate/Vpc/PublicSubnet2/Subnet", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::Subnet", + "aws:cdk:cloudformation:props": { + "vpcId": { + "Ref": "Vpc8378EB38" + }, + "availabilityZone": { + "Fn::Select": [ + 1, + { + "Fn::GetAZs": "" + } + ] + }, + "cidrBlock": "10.0.64.0/18", + "mapPublicIpOnLaunch": true, + "tags": [ + { + "key": "aws-cdk:subnet-name", + "value": "Public" + }, + { + "key": "aws-cdk:subnet-type", + "value": "Public" + }, + { + "key": "Name", + "value": "BulkUpdate/Vpc/PublicSubnet2" + } + ] + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-ec2.CfnSubnet", + "version": "0.0.0" + } + }, + "Acl": { + "id": "Acl", + "path": "BulkUpdate/Vpc/PublicSubnet2/Acl", + "constructInfo": { + "fqn": "@aws-cdk/core.Resource", + "version": "0.0.0" + } + }, + "RouteTable": { + "id": "RouteTable", + "path": "BulkUpdate/Vpc/PublicSubnet2/RouteTable", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::RouteTable", + "aws:cdk:cloudformation:props": { + "vpcId": { + "Ref": "Vpc8378EB38" + }, + "tags": [ + { + "key": "Name", + "value": "BulkUpdate/Vpc/PublicSubnet2" + } + ] + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-ec2.CfnRouteTable", + "version": "0.0.0" + } + }, + "RouteTableAssociation": { + "id": "RouteTableAssociation", + "path": "BulkUpdate/Vpc/PublicSubnet2/RouteTableAssociation", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::SubnetRouteTableAssociation", + "aws:cdk:cloudformation:props": { + "routeTableId": { + "Ref": "VpcPublicSubnet2RouteTable94F7E489" + }, + "subnetId": { + "Ref": "VpcPublicSubnet2Subnet691E08A3" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-ec2.CfnSubnetRouteTableAssociation", + "version": "0.0.0" + } + }, + "DefaultRoute": { + "id": "DefaultRoute", + "path": "BulkUpdate/Vpc/PublicSubnet2/DefaultRoute", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::Route", + "aws:cdk:cloudformation:props": { + "routeTableId": { + "Ref": "VpcPublicSubnet2RouteTable94F7E489" + }, + "destinationCidrBlock": "0.0.0.0/0", + "gatewayId": { + "Ref": "VpcIGWD7BA715C" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-ec2.CfnRoute", + "version": "0.0.0" + } + }, + "EIP": { + "id": "EIP", + "path": "BulkUpdate/Vpc/PublicSubnet2/EIP", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::EIP", + "aws:cdk:cloudformation:props": { + "domain": "vpc", + "tags": [ + { + "key": "Name", + "value": "BulkUpdate/Vpc/PublicSubnet2" + } + ] + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-ec2.CfnEIP", + "version": "0.0.0" + } + }, + "NATGateway": { + "id": "NATGateway", + "path": "BulkUpdate/Vpc/PublicSubnet2/NATGateway", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::NatGateway", + "aws:cdk:cloudformation:props": { + "subnetId": { + "Ref": "VpcPublicSubnet2Subnet691E08A3" + }, + "allocationId": { + "Fn::GetAtt": [ + "VpcPublicSubnet2EIP3C605A87", + "AllocationId" + ] + }, + "tags": [ + { + "key": "Name", + "value": "BulkUpdate/Vpc/PublicSubnet2" + } + ] + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-ec2.CfnNatGateway", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-ec2.PublicSubnet", + "version": "0.0.0" + } + }, + "PrivateSubnet1": { + "id": "PrivateSubnet1", + "path": "BulkUpdate/Vpc/PrivateSubnet1", + "children": { + "Subnet": { + "id": "Subnet", + "path": "BulkUpdate/Vpc/PrivateSubnet1/Subnet", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::Subnet", + "aws:cdk:cloudformation:props": { + "vpcId": { + "Ref": "Vpc8378EB38" + }, + "availabilityZone": { + "Fn::Select": [ + 0, + { + "Fn::GetAZs": "" + } + ] + }, + "cidrBlock": "10.0.128.0/18", + "mapPublicIpOnLaunch": false, + "tags": [ + { + "key": "aws-cdk:subnet-name", + "value": "Private" + }, + { + "key": "aws-cdk:subnet-type", + "value": "Private" + }, + { + "key": "Name", + "value": "BulkUpdate/Vpc/PrivateSubnet1" + } + ] + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-ec2.CfnSubnet", + "version": "0.0.0" + } + }, + "Acl": { + "id": "Acl", + "path": "BulkUpdate/Vpc/PrivateSubnet1/Acl", + "constructInfo": { + "fqn": "@aws-cdk/core.Resource", + "version": "0.0.0" + } + }, + "RouteTable": { + "id": "RouteTable", + "path": "BulkUpdate/Vpc/PrivateSubnet1/RouteTable", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::RouteTable", + "aws:cdk:cloudformation:props": { + "vpcId": { + "Ref": "Vpc8378EB38" + }, + "tags": [ + { + "key": "Name", + "value": "BulkUpdate/Vpc/PrivateSubnet1" + } + ] + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-ec2.CfnRouteTable", + "version": "0.0.0" + } + }, + "RouteTableAssociation": { + "id": "RouteTableAssociation", + "path": "BulkUpdate/Vpc/PrivateSubnet1/RouteTableAssociation", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::SubnetRouteTableAssociation", + "aws:cdk:cloudformation:props": { + "routeTableId": { + "Ref": "VpcPrivateSubnet1RouteTableB2C5B500" + }, + "subnetId": { + "Ref": "VpcPrivateSubnet1Subnet536B997A" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-ec2.CfnSubnetRouteTableAssociation", + "version": "0.0.0" + } + }, + "DefaultRoute": { + "id": "DefaultRoute", + "path": "BulkUpdate/Vpc/PrivateSubnet1/DefaultRoute", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::Route", + "aws:cdk:cloudformation:props": { + "routeTableId": { + "Ref": "VpcPrivateSubnet1RouteTableB2C5B500" + }, + "destinationCidrBlock": "0.0.0.0/0", + "natGatewayId": { + "Ref": "VpcPublicSubnet1NATGateway4D7517AA" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-ec2.CfnRoute", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-ec2.PrivateSubnet", + "version": "0.0.0" + } + }, + "PrivateSubnet2": { + "id": "PrivateSubnet2", + "path": "BulkUpdate/Vpc/PrivateSubnet2", + "children": { + "Subnet": { + "id": "Subnet", + "path": "BulkUpdate/Vpc/PrivateSubnet2/Subnet", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::Subnet", + "aws:cdk:cloudformation:props": { + "vpcId": { + "Ref": "Vpc8378EB38" + }, + "availabilityZone": { + "Fn::Select": [ + 1, + { + "Fn::GetAZs": "" + } + ] + }, + "cidrBlock": "10.0.192.0/18", + "mapPublicIpOnLaunch": false, + "tags": [ + { + "key": "aws-cdk:subnet-name", + "value": "Private" + }, + { + "key": "aws-cdk:subnet-type", + "value": "Private" + }, + { + "key": "Name", + "value": "BulkUpdate/Vpc/PrivateSubnet2" + } + ] + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-ec2.CfnSubnet", + "version": "0.0.0" + } + }, + "Acl": { + "id": "Acl", + "path": "BulkUpdate/Vpc/PrivateSubnet2/Acl", + "constructInfo": { + "fqn": "@aws-cdk/core.Resource", + "version": "0.0.0" + } + }, + "RouteTable": { + "id": "RouteTable", + "path": "BulkUpdate/Vpc/PrivateSubnet2/RouteTable", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::RouteTable", + "aws:cdk:cloudformation:props": { + "vpcId": { + "Ref": "Vpc8378EB38" + }, + "tags": [ + { + "key": "Name", + "value": "BulkUpdate/Vpc/PrivateSubnet2" + } + ] + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-ec2.CfnRouteTable", + "version": "0.0.0" + } + }, + "RouteTableAssociation": { + "id": "RouteTableAssociation", + "path": "BulkUpdate/Vpc/PrivateSubnet2/RouteTableAssociation", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::SubnetRouteTableAssociation", + "aws:cdk:cloudformation:props": { + "routeTableId": { + "Ref": "VpcPrivateSubnet2RouteTableA678073B" + }, + "subnetId": { + "Ref": "VpcPrivateSubnet2Subnet3788AAA1" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-ec2.CfnSubnetRouteTableAssociation", + "version": "0.0.0" + } + }, + "DefaultRoute": { + "id": "DefaultRoute", + "path": "BulkUpdate/Vpc/PrivateSubnet2/DefaultRoute", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::Route", + "aws:cdk:cloudformation:props": { + "routeTableId": { + "Ref": "VpcPrivateSubnet2RouteTableA678073B" + }, + "destinationCidrBlock": "0.0.0.0/0", + "natGatewayId": { + "Ref": "VpcPublicSubnet2NATGateway9182C01D" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-ec2.CfnRoute", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-ec2.PrivateSubnet", + "version": "0.0.0" + } + }, + "IGW": { + "id": "IGW", + "path": "BulkUpdate/Vpc/IGW", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::InternetGateway", + "aws:cdk:cloudformation:props": { + "tags": [ + { + "key": "Name", + "value": "BulkUpdate/Vpc" + } + ] + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-ec2.CfnInternetGateway", + "version": "0.0.0" + } + }, + "VPCGW": { + "id": "VPCGW", + "path": "BulkUpdate/Vpc/VPCGW", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::VPCGatewayAttachment", + "aws:cdk:cloudformation:props": { + "vpcId": { + "Ref": "Vpc8378EB38" + }, + "internetGatewayId": { + "Ref": "VpcIGWD7BA715C" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-ec2.CfnVPCGatewayAttachment", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-ec2.Vpc", + "version": "0.0.0" + } + }, + "DatabaseCluster": { + "id": "DatabaseCluster", + "path": "BulkUpdate/DatabaseCluster", + "children": { + "Subnets": { + "id": "Subnets", + "path": "BulkUpdate/DatabaseCluster/Subnets", + "children": { + "Default": { + "id": "Default", + "path": "BulkUpdate/DatabaseCluster/Subnets/Default", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::RDS::DBSubnetGroup", + "aws:cdk:cloudformation:props": { + "dbSubnetGroupDescription": "Subnets for DatabaseCluster database", + "subnetIds": [ + { + "Ref": "VpcPrivateSubnet1Subnet536B997A" + }, + { + "Ref": "VpcPrivateSubnet2Subnet3788AAA1" + } + ] + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-rds.CfnDBSubnetGroup", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-rds.SubnetGroup", + "version": "0.0.0" + } + }, + "SecurityGroup": { + "id": "SecurityGroup", + "path": "BulkUpdate/DatabaseCluster/SecurityGroup", + "children": { + "Resource": { + "id": "Resource", + "path": "BulkUpdate/DatabaseCluster/SecurityGroup/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::SecurityGroup", + "aws:cdk:cloudformation:props": { + "groupDescription": "RDS security group", + "securityGroupEgress": [ + { + "cidrIp": "0.0.0.0/0", + "description": "Allow all outbound traffic by default", + "ipProtocol": "-1" + } + ], + "vpcId": { + "Ref": "Vpc8378EB38" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-ec2.CfnSecurityGroup", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-ec2.SecurityGroup", + "version": "0.0.0" + } + }, + "Secret": { + "id": "Secret", + "path": "BulkUpdate/DatabaseCluster/Secret", + "children": { + "Resource": { + "id": "Resource", + "path": "BulkUpdate/DatabaseCluster/Secret/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::SecretsManager::Secret", + "aws:cdk:cloudformation:props": { + "description": { + "Fn::Join": [ + "", + [ + "Generated by the CDK for stack: ", + { + "Ref": "AWS::StackName" + } + ] + ] + }, + "generateSecretString": { + "passwordLength": 30, + "secretStringTemplate": "{\"username\":\"admin\"}", + "generateStringKey": "password", + "excludeCharacters": " %+~`#$&*()|[]{}:;<>?!'/@\"\\" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-secretsmanager.CfnSecret", + "version": "0.0.0" + } + }, + "Attachment": { + "id": "Attachment", + "path": "BulkUpdate/DatabaseCluster/Secret/Attachment", + "children": { + "Resource": { + "id": "Resource", + "path": "BulkUpdate/DatabaseCluster/Secret/Attachment/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::SecretsManager::SecretTargetAttachment", + "aws:cdk:cloudformation:props": { + "secretId": { + "Ref": "DatabaseClusterSecret3F333A5B" + }, + "targetId": { + "Ref": "DatabaseCluster68FC2945" + }, + "targetType": "AWS::RDS::DBCluster" + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-secretsmanager.CfnSecretTargetAttachment", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-secretsmanager.SecretTargetAttachment", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-rds.DatabaseSecret", + "version": "0.0.0" + } + }, + "Resource": { + "id": "Resource", + "path": "BulkUpdate/DatabaseCluster/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::RDS::DBCluster", + "aws:cdk:cloudformation:props": { + "engine": "aurora", + "copyTagsToSnapshot": true, + "dbSubnetGroupName": { + "Ref": "DatabaseClusterSubnets7FAE1846" + }, + "masterUsername": { + "Fn::Join": [ + "", + [ + "{{resolve:secretsmanager:", + { + "Ref": "DatabaseClusterSecret3F333A5B" + }, + ":SecretString:username::}}" + ] + ] + }, + "masterUserPassword": { + "Fn::Join": [ + "", + [ + "{{resolve:secretsmanager:", + { + "Ref": "DatabaseClusterSecret3F333A5B" + }, + ":SecretString:password::}}" + ] + ] + }, + "vpcSecurityGroupIds": [ + { + "Fn::GetAtt": [ + "DatabaseClusterSecurityGroupCBE34284", + "GroupId" + ] + } + ] + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-rds.CfnDBCluster", + "version": "0.0.0" + } + }, + "Instance1": { + "id": "Instance1", + "path": "BulkUpdate/DatabaseCluster/Instance1", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::RDS::DBInstance", + "aws:cdk:cloudformation:props": { + "dbInstanceClass": "db.t3.small", + "dbClusterIdentifier": { + "Ref": "DatabaseCluster68FC2945" + }, + "dbSubnetGroupName": { + "Ref": "DatabaseClusterSubnets7FAE1846" + }, + "engine": "aurora" + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-rds.CfnDBInstance", + "version": "0.0.0" + } + }, + "Instance2": { + "id": "Instance2", + "path": "BulkUpdate/DatabaseCluster/Instance2", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::RDS::DBInstance", + "aws:cdk:cloudformation:props": { + "dbInstanceClass": "db.t3.small", + "dbClusterIdentifier": { + "Ref": "DatabaseCluster68FC2945" + }, + "dbSubnetGroupName": { + "Ref": "DatabaseClusterSubnets7FAE1846" + }, + "engine": "aurora" + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-rds.CfnDBInstance", + "version": "0.0.0" + } + }, + "Instance3": { + "id": "Instance3", + "path": "BulkUpdate/DatabaseCluster/Instance3", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::RDS::DBInstance", + "aws:cdk:cloudformation:props": { + "dbInstanceClass": "db.t3.small", + "dbClusterIdentifier": { + "Ref": "DatabaseCluster68FC2945" + }, + "dbSubnetGroupName": { + "Ref": "DatabaseClusterSubnets7FAE1846" + }, + "engine": "aurora" + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-rds.CfnDBInstance", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-rds.DatabaseCluster", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/core.Stack", + "version": "0.0.0" + } + }, + "RollingUpdate": { + "id": "RollingUpdate", + "path": "RollingUpdate", + "children": { + "Vpc": { + "id": "Vpc", + "path": "RollingUpdate/Vpc", + "children": { + "Resource": { + "id": "Resource", + "path": "RollingUpdate/Vpc/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::VPC", + "aws:cdk:cloudformation:props": { + "cidrBlock": "10.0.0.0/16", + "enableDnsHostnames": true, + "enableDnsSupport": true, + "instanceTenancy": "default", + "tags": [ + { + "key": "Name", + "value": "RollingUpdate/Vpc" + } + ] + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-ec2.CfnVPC", + "version": "0.0.0" + } + }, + "PublicSubnet1": { + "id": "PublicSubnet1", + "path": "RollingUpdate/Vpc/PublicSubnet1", + "children": { + "Subnet": { + "id": "Subnet", + "path": "RollingUpdate/Vpc/PublicSubnet1/Subnet", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::Subnet", + "aws:cdk:cloudformation:props": { + "vpcId": { + "Ref": "Vpc8378EB38" + }, + "availabilityZone": { + "Fn::Select": [ + 0, + { + "Fn::GetAZs": "" + } + ] + }, + "cidrBlock": "10.0.0.0/18", + "mapPublicIpOnLaunch": true, + "tags": [ + { + "key": "aws-cdk:subnet-name", + "value": "Public" + }, + { + "key": "aws-cdk:subnet-type", + "value": "Public" + }, + { + "key": "Name", + "value": "RollingUpdate/Vpc/PublicSubnet1" + } + ] + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-ec2.CfnSubnet", + "version": "0.0.0" + } + }, + "Acl": { + "id": "Acl", + "path": "RollingUpdate/Vpc/PublicSubnet1/Acl", + "constructInfo": { + "fqn": "@aws-cdk/core.Resource", + "version": "0.0.0" + } + }, + "RouteTable": { + "id": "RouteTable", + "path": "RollingUpdate/Vpc/PublicSubnet1/RouteTable", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::RouteTable", + "aws:cdk:cloudformation:props": { + "vpcId": { + "Ref": "Vpc8378EB38" + }, + "tags": [ + { + "key": "Name", + "value": "RollingUpdate/Vpc/PublicSubnet1" + } + ] + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-ec2.CfnRouteTable", + "version": "0.0.0" + } + }, + "RouteTableAssociation": { + "id": "RouteTableAssociation", + "path": "RollingUpdate/Vpc/PublicSubnet1/RouteTableAssociation", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::SubnetRouteTableAssociation", + "aws:cdk:cloudformation:props": { + "routeTableId": { + "Ref": "VpcPublicSubnet1RouteTable6C95E38E" + }, + "subnetId": { + "Ref": "VpcPublicSubnet1Subnet5C2D37C4" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-ec2.CfnSubnetRouteTableAssociation", + "version": "0.0.0" + } + }, + "DefaultRoute": { + "id": "DefaultRoute", + "path": "RollingUpdate/Vpc/PublicSubnet1/DefaultRoute", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::Route", + "aws:cdk:cloudformation:props": { + "routeTableId": { + "Ref": "VpcPublicSubnet1RouteTable6C95E38E" + }, + "destinationCidrBlock": "0.0.0.0/0", + "gatewayId": { + "Ref": "VpcIGWD7BA715C" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-ec2.CfnRoute", + "version": "0.0.0" + } + }, + "EIP": { + "id": "EIP", + "path": "RollingUpdate/Vpc/PublicSubnet1/EIP", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::EIP", + "aws:cdk:cloudformation:props": { + "domain": "vpc", + "tags": [ + { + "key": "Name", + "value": "RollingUpdate/Vpc/PublicSubnet1" + } + ] + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-ec2.CfnEIP", + "version": "0.0.0" + } + }, + "NATGateway": { + "id": "NATGateway", + "path": "RollingUpdate/Vpc/PublicSubnet1/NATGateway", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::NatGateway", + "aws:cdk:cloudformation:props": { + "subnetId": { + "Ref": "VpcPublicSubnet1Subnet5C2D37C4" + }, + "allocationId": { + "Fn::GetAtt": [ + "VpcPublicSubnet1EIPD7E02669", + "AllocationId" + ] + }, + "tags": [ + { + "key": "Name", + "value": "RollingUpdate/Vpc/PublicSubnet1" + } + ] + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-ec2.CfnNatGateway", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-ec2.PublicSubnet", + "version": "0.0.0" + } + }, + "PublicSubnet2": { + "id": "PublicSubnet2", + "path": "RollingUpdate/Vpc/PublicSubnet2", + "children": { + "Subnet": { + "id": "Subnet", + "path": "RollingUpdate/Vpc/PublicSubnet2/Subnet", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::Subnet", + "aws:cdk:cloudformation:props": { + "vpcId": { + "Ref": "Vpc8378EB38" + }, + "availabilityZone": { + "Fn::Select": [ + 1, + { + "Fn::GetAZs": "" + } + ] + }, + "cidrBlock": "10.0.64.0/18", + "mapPublicIpOnLaunch": true, + "tags": [ + { + "key": "aws-cdk:subnet-name", + "value": "Public" + }, + { + "key": "aws-cdk:subnet-type", + "value": "Public" + }, + { + "key": "Name", + "value": "RollingUpdate/Vpc/PublicSubnet2" + } + ] + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-ec2.CfnSubnet", + "version": "0.0.0" + } + }, + "Acl": { + "id": "Acl", + "path": "RollingUpdate/Vpc/PublicSubnet2/Acl", + "constructInfo": { + "fqn": "@aws-cdk/core.Resource", + "version": "0.0.0" + } + }, + "RouteTable": { + "id": "RouteTable", + "path": "RollingUpdate/Vpc/PublicSubnet2/RouteTable", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::RouteTable", + "aws:cdk:cloudformation:props": { + "vpcId": { + "Ref": "Vpc8378EB38" + }, + "tags": [ + { + "key": "Name", + "value": "RollingUpdate/Vpc/PublicSubnet2" + } + ] + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-ec2.CfnRouteTable", + "version": "0.0.0" + } + }, + "RouteTableAssociation": { + "id": "RouteTableAssociation", + "path": "RollingUpdate/Vpc/PublicSubnet2/RouteTableAssociation", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::SubnetRouteTableAssociation", + "aws:cdk:cloudformation:props": { + "routeTableId": { + "Ref": "VpcPublicSubnet2RouteTable94F7E489" + }, + "subnetId": { + "Ref": "VpcPublicSubnet2Subnet691E08A3" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-ec2.CfnSubnetRouteTableAssociation", + "version": "0.0.0" + } + }, + "DefaultRoute": { + "id": "DefaultRoute", + "path": "RollingUpdate/Vpc/PublicSubnet2/DefaultRoute", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::Route", + "aws:cdk:cloudformation:props": { + "routeTableId": { + "Ref": "VpcPublicSubnet2RouteTable94F7E489" + }, + "destinationCidrBlock": "0.0.0.0/0", + "gatewayId": { + "Ref": "VpcIGWD7BA715C" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-ec2.CfnRoute", + "version": "0.0.0" + } + }, + "EIP": { + "id": "EIP", + "path": "RollingUpdate/Vpc/PublicSubnet2/EIP", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::EIP", + "aws:cdk:cloudformation:props": { + "domain": "vpc", + "tags": [ + { + "key": "Name", + "value": "RollingUpdate/Vpc/PublicSubnet2" + } + ] + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-ec2.CfnEIP", + "version": "0.0.0" + } + }, + "NATGateway": { + "id": "NATGateway", + "path": "RollingUpdate/Vpc/PublicSubnet2/NATGateway", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::NatGateway", + "aws:cdk:cloudformation:props": { + "subnetId": { + "Ref": "VpcPublicSubnet2Subnet691E08A3" + }, + "allocationId": { + "Fn::GetAtt": [ + "VpcPublicSubnet2EIP3C605A87", + "AllocationId" + ] + }, + "tags": [ + { + "key": "Name", + "value": "RollingUpdate/Vpc/PublicSubnet2" + } + ] + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-ec2.CfnNatGateway", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-ec2.PublicSubnet", + "version": "0.0.0" + } + }, + "PrivateSubnet1": { + "id": "PrivateSubnet1", + "path": "RollingUpdate/Vpc/PrivateSubnet1", + "children": { + "Subnet": { + "id": "Subnet", + "path": "RollingUpdate/Vpc/PrivateSubnet1/Subnet", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::Subnet", + "aws:cdk:cloudformation:props": { + "vpcId": { + "Ref": "Vpc8378EB38" + }, + "availabilityZone": { + "Fn::Select": [ + 0, + { + "Fn::GetAZs": "" + } + ] + }, + "cidrBlock": "10.0.128.0/18", + "mapPublicIpOnLaunch": false, + "tags": [ + { + "key": "aws-cdk:subnet-name", + "value": "Private" + }, + { + "key": "aws-cdk:subnet-type", + "value": "Private" + }, + { + "key": "Name", + "value": "RollingUpdate/Vpc/PrivateSubnet1" + } + ] + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-ec2.CfnSubnet", + "version": "0.0.0" + } + }, + "Acl": { + "id": "Acl", + "path": "RollingUpdate/Vpc/PrivateSubnet1/Acl", + "constructInfo": { + "fqn": "@aws-cdk/core.Resource", + "version": "0.0.0" + } + }, + "RouteTable": { + "id": "RouteTable", + "path": "RollingUpdate/Vpc/PrivateSubnet1/RouteTable", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::RouteTable", + "aws:cdk:cloudformation:props": { + "vpcId": { + "Ref": "Vpc8378EB38" + }, + "tags": [ + { + "key": "Name", + "value": "RollingUpdate/Vpc/PrivateSubnet1" + } + ] + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-ec2.CfnRouteTable", + "version": "0.0.0" + } + }, + "RouteTableAssociation": { + "id": "RouteTableAssociation", + "path": "RollingUpdate/Vpc/PrivateSubnet1/RouteTableAssociation", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::SubnetRouteTableAssociation", + "aws:cdk:cloudformation:props": { + "routeTableId": { + "Ref": "VpcPrivateSubnet1RouteTableB2C5B500" + }, + "subnetId": { + "Ref": "VpcPrivateSubnet1Subnet536B997A" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-ec2.CfnSubnetRouteTableAssociation", + "version": "0.0.0" + } + }, + "DefaultRoute": { + "id": "DefaultRoute", + "path": "RollingUpdate/Vpc/PrivateSubnet1/DefaultRoute", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::Route", + "aws:cdk:cloudformation:props": { + "routeTableId": { + "Ref": "VpcPrivateSubnet1RouteTableB2C5B500" + }, + "destinationCidrBlock": "0.0.0.0/0", + "natGatewayId": { + "Ref": "VpcPublicSubnet1NATGateway4D7517AA" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-ec2.CfnRoute", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-ec2.PrivateSubnet", + "version": "0.0.0" + } + }, + "PrivateSubnet2": { + "id": "PrivateSubnet2", + "path": "RollingUpdate/Vpc/PrivateSubnet2", + "children": { + "Subnet": { + "id": "Subnet", + "path": "RollingUpdate/Vpc/PrivateSubnet2/Subnet", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::Subnet", + "aws:cdk:cloudformation:props": { + "vpcId": { + "Ref": "Vpc8378EB38" + }, + "availabilityZone": { + "Fn::Select": [ + 1, + { + "Fn::GetAZs": "" + } + ] + }, + "cidrBlock": "10.0.192.0/18", + "mapPublicIpOnLaunch": false, + "tags": [ + { + "key": "aws-cdk:subnet-name", + "value": "Private" + }, + { + "key": "aws-cdk:subnet-type", + "value": "Private" + }, + { + "key": "Name", + "value": "RollingUpdate/Vpc/PrivateSubnet2" + } + ] + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-ec2.CfnSubnet", + "version": "0.0.0" + } + }, + "Acl": { + "id": "Acl", + "path": "RollingUpdate/Vpc/PrivateSubnet2/Acl", + "constructInfo": { + "fqn": "@aws-cdk/core.Resource", + "version": "0.0.0" + } + }, + "RouteTable": { + "id": "RouteTable", + "path": "RollingUpdate/Vpc/PrivateSubnet2/RouteTable", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::RouteTable", + "aws:cdk:cloudformation:props": { + "vpcId": { + "Ref": "Vpc8378EB38" + }, + "tags": [ + { + "key": "Name", + "value": "RollingUpdate/Vpc/PrivateSubnet2" + } + ] + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-ec2.CfnRouteTable", + "version": "0.0.0" + } + }, + "RouteTableAssociation": { + "id": "RouteTableAssociation", + "path": "RollingUpdate/Vpc/PrivateSubnet2/RouteTableAssociation", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::SubnetRouteTableAssociation", + "aws:cdk:cloudformation:props": { + "routeTableId": { + "Ref": "VpcPrivateSubnet2RouteTableA678073B" + }, + "subnetId": { + "Ref": "VpcPrivateSubnet2Subnet3788AAA1" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-ec2.CfnSubnetRouteTableAssociation", + "version": "0.0.0" + } + }, + "DefaultRoute": { + "id": "DefaultRoute", + "path": "RollingUpdate/Vpc/PrivateSubnet2/DefaultRoute", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::Route", + "aws:cdk:cloudformation:props": { + "routeTableId": { + "Ref": "VpcPrivateSubnet2RouteTableA678073B" + }, + "destinationCidrBlock": "0.0.0.0/0", + "natGatewayId": { + "Ref": "VpcPublicSubnet2NATGateway9182C01D" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-ec2.CfnRoute", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-ec2.PrivateSubnet", + "version": "0.0.0" + } + }, + "IGW": { + "id": "IGW", + "path": "RollingUpdate/Vpc/IGW", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::InternetGateway", + "aws:cdk:cloudformation:props": { + "tags": [ + { + "key": "Name", + "value": "RollingUpdate/Vpc" + } + ] + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-ec2.CfnInternetGateway", + "version": "0.0.0" + } + }, + "VPCGW": { + "id": "VPCGW", + "path": "RollingUpdate/Vpc/VPCGW", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::VPCGatewayAttachment", + "aws:cdk:cloudformation:props": { + "vpcId": { + "Ref": "Vpc8378EB38" + }, + "internetGatewayId": { + "Ref": "VpcIGWD7BA715C" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-ec2.CfnVPCGatewayAttachment", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-ec2.Vpc", + "version": "0.0.0" + } + }, + "DatabaseCluster": { + "id": "DatabaseCluster", + "path": "RollingUpdate/DatabaseCluster", + "children": { + "Subnets": { + "id": "Subnets", + "path": "RollingUpdate/DatabaseCluster/Subnets", + "children": { + "Default": { + "id": "Default", + "path": "RollingUpdate/DatabaseCluster/Subnets/Default", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::RDS::DBSubnetGroup", + "aws:cdk:cloudformation:props": { + "dbSubnetGroupDescription": "Subnets for DatabaseCluster database", + "subnetIds": [ + { + "Ref": "VpcPrivateSubnet1Subnet536B997A" + }, + { + "Ref": "VpcPrivateSubnet2Subnet3788AAA1" + } + ] + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-rds.CfnDBSubnetGroup", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-rds.SubnetGroup", + "version": "0.0.0" + } + }, + "SecurityGroup": { + "id": "SecurityGroup", + "path": "RollingUpdate/DatabaseCluster/SecurityGroup", + "children": { + "Resource": { + "id": "Resource", + "path": "RollingUpdate/DatabaseCluster/SecurityGroup/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::SecurityGroup", + "aws:cdk:cloudformation:props": { + "groupDescription": "RDS security group", + "securityGroupEgress": [ + { + "cidrIp": "0.0.0.0/0", + "description": "Allow all outbound traffic by default", + "ipProtocol": "-1" + } + ], + "vpcId": { + "Ref": "Vpc8378EB38" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-ec2.CfnSecurityGroup", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-ec2.SecurityGroup", + "version": "0.0.0" + } + }, + "Secret": { + "id": "Secret", + "path": "RollingUpdate/DatabaseCluster/Secret", + "children": { + "Resource": { + "id": "Resource", + "path": "RollingUpdate/DatabaseCluster/Secret/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::SecretsManager::Secret", + "aws:cdk:cloudformation:props": { + "description": { + "Fn::Join": [ + "", + [ + "Generated by the CDK for stack: ", + { + "Ref": "AWS::StackName" + } + ] + ] + }, + "generateSecretString": { + "passwordLength": 30, + "secretStringTemplate": "{\"username\":\"admin\"}", + "generateStringKey": "password", + "excludeCharacters": " %+~`#$&*()|[]{}:;<>?!'/@\"\\" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-secretsmanager.CfnSecret", + "version": "0.0.0" + } + }, + "Attachment": { + "id": "Attachment", + "path": "RollingUpdate/DatabaseCluster/Secret/Attachment", + "children": { + "Resource": { + "id": "Resource", + "path": "RollingUpdate/DatabaseCluster/Secret/Attachment/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::SecretsManager::SecretTargetAttachment", + "aws:cdk:cloudformation:props": { + "secretId": { + "Ref": "DatabaseClusterSecret3F333A5B" + }, + "targetId": { + "Ref": "DatabaseCluster68FC2945" + }, + "targetType": "AWS::RDS::DBCluster" + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-secretsmanager.CfnSecretTargetAttachment", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-secretsmanager.SecretTargetAttachment", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-rds.DatabaseSecret", + "version": "0.0.0" + } + }, + "Resource": { + "id": "Resource", + "path": "RollingUpdate/DatabaseCluster/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::RDS::DBCluster", + "aws:cdk:cloudformation:props": { + "engine": "aurora", + "copyTagsToSnapshot": true, + "dbSubnetGroupName": { + "Ref": "DatabaseClusterSubnets7FAE1846" + }, + "masterUsername": { + "Fn::Join": [ + "", + [ + "{{resolve:secretsmanager:", + { + "Ref": "DatabaseClusterSecret3F333A5B" + }, + ":SecretString:username::}}" + ] + ] + }, + "masterUserPassword": { + "Fn::Join": [ + "", + [ + "{{resolve:secretsmanager:", + { + "Ref": "DatabaseClusterSecret3F333A5B" + }, + ":SecretString:password::}}" + ] + ] + }, + "vpcSecurityGroupIds": [ + { + "Fn::GetAtt": [ + "DatabaseClusterSecurityGroupCBE34284", + "GroupId" + ] + } + ] + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-rds.CfnDBCluster", + "version": "0.0.0" + } + }, + "Instance1": { + "id": "Instance1", + "path": "RollingUpdate/DatabaseCluster/Instance1", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::RDS::DBInstance", + "aws:cdk:cloudformation:props": { + "dbInstanceClass": "db.t3.small", + "dbClusterIdentifier": { + "Ref": "DatabaseCluster68FC2945" + }, + "dbSubnetGroupName": { + "Ref": "DatabaseClusterSubnets7FAE1846" + }, + "engine": "aurora" + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-rds.CfnDBInstance", + "version": "0.0.0" + } + }, + "Instance2": { + "id": "Instance2", + "path": "RollingUpdate/DatabaseCluster/Instance2", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::RDS::DBInstance", + "aws:cdk:cloudformation:props": { + "dbInstanceClass": "db.t3.small", + "dbClusterIdentifier": { + "Ref": "DatabaseCluster68FC2945" + }, + "dbSubnetGroupName": { + "Ref": "DatabaseClusterSubnets7FAE1846" + }, + "engine": "aurora" + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-rds.CfnDBInstance", + "version": "0.0.0" + } + }, + "Instance3": { + "id": "Instance3", + "path": "RollingUpdate/DatabaseCluster/Instance3", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::RDS::DBInstance", + "aws:cdk:cloudformation:props": { + "dbInstanceClass": "db.t3.small", + "dbClusterIdentifier": { + "Ref": "DatabaseCluster68FC2945" + }, + "dbSubnetGroupName": { + "Ref": "DatabaseClusterSubnets7FAE1846" + }, + "engine": "aurora" + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-rds.CfnDBInstance", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-rds.DatabaseCluster", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/core.Stack", + "version": "0.0.0" + } + }, + "InstanceUpdateBehaviorTests": { + "id": "InstanceUpdateBehaviorTests", + "path": "InstanceUpdateBehaviorTests", + "children": { + "DefaultTest": { + "id": "DefaultTest", + "path": "InstanceUpdateBehaviorTests/DefaultTest", + "children": { + "Default": { + "id": "Default", + "path": "InstanceUpdateBehaviorTests/DefaultTest/Default", + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.1.33" + } + }, + "DeployAssert": { + "id": "DeployAssert", + "path": "InstanceUpdateBehaviorTests/DefaultTest/DeployAssert", + "constructInfo": { + "fqn": "@aws-cdk/core.Stack", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/integ-tests.IntegTestCase", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/integ-tests.IntegTest", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/core.App", + "version": "0.0.0" + } + } +} \ No newline at end of file From ddfca48bb47d7bfe8b08827487e228aa4f149e43 Mon Sep 17 00:00:00 2001 From: Momo Kornher Date: Wed, 13 Jul 2022 14:13:00 +0100 Subject: [PATCH 28/92] fix(custom-resources): Custom resource provider framework not passing `ResponseURL` to user function (#21117) aws#20889 included a change that broke the custom resource framework by not including the necessary presigned URL. We attempted to fix this with aws#21065 but it didn't resolve the issue and aws#21109 reverted the attempted fix. This changes explicitly includes the presigned URL, as well as adding tests to ensure the URL is passed to the downstream Lambda Function. Closes aws#21058 ---- ### All Submissions: * [x] Have you followed the guidelines in our [Contributing guide?](https://github.com/aws/aws-cdk/blob/main/CONTRIBUTING.md) ### Adding new Unconventional Dependencies: * [ ] This PR adds new unconventional dependencies following the process described [here](https://github.com/aws/aws-cdk/blob/main/CONTRIBUTING.md/#adding-new-unconventional-dependencies) ### New Features * [ ] Have you added the new feature to an [integration test](https://github.com/aws/aws-cdk/blob/main/INTEGRATION_TESTS.md)? * [ ] Did you use `yarn integ` to deploy the infrastructure and generate the snapshot (i.e. `yarn integ` without `--dry-run`)? *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- .../provider-framework/runtime/framework.ts | 10 ++--- .../test/provider-framework/runtime.test.ts | 38 +++++++++++++++++++ 2 files changed, 43 insertions(+), 5 deletions(-) diff --git a/packages/@aws-cdk/custom-resources/lib/provider-framework/runtime/framework.ts b/packages/@aws-cdk/custom-resources/lib/provider-framework/runtime/framework.ts index 97b7c4de61e4b..20c0144311b94 100644 --- a/packages/@aws-cdk/custom-resources/lib/provider-framework/runtime/framework.ts +++ b/packages/@aws-cdk/custom-resources/lib/provider-framework/runtime/framework.ts @@ -29,7 +29,7 @@ async function onEvent(cfnRequest: AWSLambda.CloudFormationCustomResourceEvent) cfnRequest.ResourceProperties = cfnRequest.ResourceProperties || { }; - const onEventResult = await invokeUserFunction(consts.USER_ON_EVENT_FUNCTION_ARN_ENV, sanitizedRequest) as OnEventResponse; + const onEventResult = await invokeUserFunction(consts.USER_ON_EVENT_FUNCTION_ARN_ENV, sanitizedRequest, cfnRequest.ResponseURL) as OnEventResponse; log('onEvent returned:', onEventResult); // merge the request and the result from onEvent to form the complete resource event @@ -61,7 +61,7 @@ async function isComplete(event: AWSCDKAsyncCustomResource.IsCompleteRequest) { const sanitizedRequest = { ...event, ResponseURL: '...' } as const; log('isComplete', sanitizedRequest); - const isCompleteResult = await invokeUserFunction(consts.USER_IS_COMPLETE_FUNCTION_ARN_ENV, sanitizedRequest) as IsCompleteResponse; + const isCompleteResult = await invokeUserFunction(consts.USER_IS_COMPLETE_FUNCTION_ARN_ENV, sanitizedRequest, event.ResponseURL) as IsCompleteResponse; log('user isComplete returned:', isCompleteResult); // if we are not complete, return false, and don't send a response back. @@ -96,7 +96,7 @@ async function onTimeout(timeoutEvent: any) { }); } -async function invokeUserFunction(functionArnEnv: string, sanitizedPayload: A) { +async function invokeUserFunction(functionArnEnv: string, sanitizedPayload: A, responseUrl: string) { const functionArn = getEnv(functionArnEnv); log(`executing user function ${functionArn} with payload`, sanitizedPayload); @@ -106,8 +106,8 @@ async function invokeUserFunction(functionArnE const resp = await invokeFunction({ FunctionName: functionArn, - // Strip 'ResponseURL' -- the downstream CR doesn't need it and can only log it by accident - Payload: JSON.stringify({ ...sanitizedPayload, ResponseURL: undefined }), + // Cannot strip 'ResponseURL' here as this would be a breaking change even though the downstream CR doesn't need it + Payload: JSON.stringify({ ...sanitizedPayload, ResponseURL: responseUrl }), }); log('user function response:', resp, typeof(resp)); diff --git a/packages/@aws-cdk/custom-resources/test/provider-framework/runtime.test.ts b/packages/@aws-cdk/custom-resources/test/provider-framework/runtime.test.ts index 751e33e6408e0..2d35a9e63eac9 100644 --- a/packages/@aws-cdk/custom-resources/test/provider-framework/runtime.test.ts +++ b/packages/@aws-cdk/custom-resources/test/provider-framework/runtime.test.ts @@ -18,7 +18,10 @@ outbound.httpRequest = mocks.httpRequestMock; outbound.invokeFunction = mocks.invokeFunctionMock; outbound.startExecution = mocks.startExecutionMock; +const invokeFunctionSpy = jest.spyOn(outbound, 'invokeFunction'); + beforeEach(() => mocks.setup()); +afterEach(() => invokeFunctionSpy.mockClear()); test('async flow: isComplete returns true only after 3 times', async () => { let isCompleteCalls = 0; @@ -346,6 +349,41 @@ describe('if CREATE fails, the subsequent DELETE will be ignored', () => { }); +describe('ResponseURL is passed to user function', () => { + test('for onEvent', async () => { + // GIVEN + mocks.onEventImplMock = async () => ({ PhysicalResourceId: MOCK_PHYSICAL_ID }); + + // WHEN + await simulateEvent({ + RequestType: 'Create', + }); + + // THEN + expect(invokeFunctionSpy).toHaveBeenCalledTimes(1); + expect(invokeFunctionSpy).toBeCalledWith(expect.objectContaining({ + Payload: expect.stringContaining(`"ResponseURL":"${mocks.MOCK_REQUEST.ResponseURL}"`), + })); + }); + + test('for isComplete', async () => { + // GIVEN + mocks.onEventImplMock = async () => ({ PhysicalResourceId: MOCK_PHYSICAL_ID }); + mocks.isCompleteImplMock = async () => ({ IsComplete: true }); + + // WHEN + await simulateEvent({ + RequestType: 'Create', + }); + + // THEN + expect(invokeFunctionSpy).toHaveBeenCalledTimes(2); + expect(invokeFunctionSpy).toHaveBeenLastCalledWith(expect.objectContaining({ + Payload: expect.stringContaining(`"ResponseURL":"${mocks.MOCK_REQUEST.ResponseURL}"`), + })); + }); +}); + // ----------------------------------------------------------------------------------------------------------------------- /** From 541ce1b46e5e21764d5f58ef73f46946bfd68cd7 Mon Sep 17 00:00:00 2001 From: Jonathan Goldwasser Date: Wed, 13 Jul 2022 17:19:56 +0200 Subject: [PATCH 29/92] feat(ses): DedicatedIpPool, ConfigurationSet and EmailIdentity (#20997) Add L2 constructs for `DedidactedIpPool`, `ConfigurationSet` and `EmailIdentity`. `EmailIdentity` can be created for a hosted zone. In this case, all the necessary DKIM and MAIL FROM records are created automatically. ---- ### All Submissions: * [x] Have you followed the guidelines in our [Contributing guide?](https://github.com/aws/aws-cdk/blob/main/CONTRIBUTING.md) ### Adding new Unconventional Dependencies: * [ ] This PR adds new unconventional dependencies following the process described [here](https://github.com/aws/aws-cdk/blob/main/CONTRIBUTING.md/#adding-new-unconventional-dependencies) ### New Features * [x] Have you added the new feature to an [integration test](https://github.com/aws/aws-cdk/blob/main/INTEGRATION_TESTS.md)? * [x] Did you use `yarn integ` to deploy the infrastructure and generate the snapshot (i.e. `yarn integ` without `--dry-run`)? *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- packages/@aws-cdk/aws-ses/README.md | 92 +++- .../@aws-cdk/aws-ses/lib/configuration-set.ts | 171 +++++++ .../@aws-cdk/aws-ses/lib/dedicated-ip-pool.ts | 56 +++ .../@aws-cdk/aws-ses/lib/email-identity.ts | 444 ++++++++++++++++++ packages/@aws-cdk/aws-ses/lib/index.ts | 3 + .../@aws-cdk/aws-ses/lib/private/utils.ts | 4 + packages/@aws-cdk/aws-ses/package.json | 6 +- .../aws-ses/rosetta/default.ts-fixture | 3 +- ...aultTestDeployAssert9B6AD46A.template.json | 1 + ...-ses-configuration-set-integ.template.json | 7 + .../configuration-set.integ.snapshot/cdk.out | 1 + .../integ.json | 11 + .../manifest.json | 37 ++ .../tree.json | 89 ++++ .../aws-ses/test/configuration-set.test.ts | 42 ++ .../aws-ses/test/dedicated-ip-pool.test.ts | 17 + ...aultTestDeployAssert3F909307.template.json | 1 + ...cdk-ses-email-identity-integ.template.json | 154 ++++++ .../email-identity.integ.snapshot/cdk.out | 1 + .../email-identity.integ.snapshot/integ.json | 11 + .../manifest.json | 73 +++ .../email-identity.integ.snapshot/tree.json | 343 ++++++++++++++ .../aws-ses/test/email-identity.test.ts | 217 +++++++++ .../aws-ses/test/integ.configuration-set.ts | 20 + .../aws-ses/test/integ.email-identity.ts | 28 ++ packages/@aws-cdk/custom-resources/README.md | 27 -- 26 files changed, 1829 insertions(+), 30 deletions(-) create mode 100644 packages/@aws-cdk/aws-ses/lib/configuration-set.ts create mode 100644 packages/@aws-cdk/aws-ses/lib/dedicated-ip-pool.ts create mode 100644 packages/@aws-cdk/aws-ses/lib/email-identity.ts create mode 100644 packages/@aws-cdk/aws-ses/lib/private/utils.ts create mode 100644 packages/@aws-cdk/aws-ses/test/configuration-set.integ.snapshot/ConfigurationSetIntegDefaultTestDeployAssert9B6AD46A.template.json create mode 100644 packages/@aws-cdk/aws-ses/test/configuration-set.integ.snapshot/cdk-ses-configuration-set-integ.template.json create mode 100644 packages/@aws-cdk/aws-ses/test/configuration-set.integ.snapshot/cdk.out create mode 100644 packages/@aws-cdk/aws-ses/test/configuration-set.integ.snapshot/integ.json create mode 100644 packages/@aws-cdk/aws-ses/test/configuration-set.integ.snapshot/manifest.json create mode 100644 packages/@aws-cdk/aws-ses/test/configuration-set.integ.snapshot/tree.json create mode 100644 packages/@aws-cdk/aws-ses/test/configuration-set.test.ts create mode 100644 packages/@aws-cdk/aws-ses/test/dedicated-ip-pool.test.ts create mode 100644 packages/@aws-cdk/aws-ses/test/email-identity.integ.snapshot/EmailIdentityIntegDefaultTestDeployAssert3F909307.template.json create mode 100644 packages/@aws-cdk/aws-ses/test/email-identity.integ.snapshot/cdk-ses-email-identity-integ.template.json create mode 100644 packages/@aws-cdk/aws-ses/test/email-identity.integ.snapshot/cdk.out create mode 100644 packages/@aws-cdk/aws-ses/test/email-identity.integ.snapshot/integ.json create mode 100644 packages/@aws-cdk/aws-ses/test/email-identity.integ.snapshot/manifest.json create mode 100644 packages/@aws-cdk/aws-ses/test/email-identity.integ.snapshot/tree.json create mode 100644 packages/@aws-cdk/aws-ses/test/email-identity.test.ts create mode 100644 packages/@aws-cdk/aws-ses/test/integ.configuration-set.ts create mode 100644 packages/@aws-cdk/aws-ses/test/integ.email-identity.ts diff --git a/packages/@aws-cdk/aws-ses/README.md b/packages/@aws-cdk/aws-ses/README.md index 04fc9f2e26202..a4609fe075ccf 100644 --- a/packages/@aws-cdk/aws-ses/README.md +++ b/packages/@aws-cdk/aws-ses/README.md @@ -90,7 +90,7 @@ new ses.ReceiptRuleSet(this, 'RuleSet', { This will add a rule at the top of the rule set with a Lambda action that stops processing messages that have at least one spam indicator. See [Lambda Function Examples](https://docs.aws.amazon.com/ses/latest/DeveloperGuide/receiving-email-action-lambda-example-functions.html). -## Receipt filter +### Receipt filter Create a receipt filter: @@ -112,3 +112,93 @@ new ses.AllowListReceiptFilter(this, 'AllowList', { ``` This will first create a block all filter and then create allow filters for the listed ip addresses. + +## Email sending + +### Dedicated IP pools + +When you create a new Amazon SES account, your emails are sent from IP addresses that are shared with other +Amazon SES users. For [an additional monthly charge](https://aws.amazon.com/ses/pricing/), you can lease +dedicated IP addresses that are reserved for your exclusive use. + +Use the `DedicatedIpPool` construct to create a pool of dedicated IP addresses: + +```ts +new ses.DedicatedIpPool(this, 'Pool'); +``` + +The pool can then be used in a configuration set. + +### Configuration sets + +Configuration sets are groups of rules that you can apply to your verified identities. A verified identity is +a domain, subdomain, or email address you use to send email through Amazon SES. When you apply a configuration +set to an email, all of the rules in that configuration set are applied to the email. + +Use the `ConfigurationSet` construct to create a configuration set: + +```ts +declare const myPool: ses.IDedicatedIpPool; + +new ses.ConfigurationSet(this, 'ConfigurationSet', { + customTrackingRedirectDomain: 'track.cdk.dev', + suppressionReasons: ses.SuppressionReasons.COMPLAINTS_ONLY, + tlsPolicy: ses.ConfigurationSetTlsPolicy.REQUIRE, + dedicatedIpPool: myPool, +}); +``` + +### Email identity + +In Amazon SES, a verified identity is a domain or email address that you use to send or receive email. Before you +can send an email using Amazon SES, you must create and verify each identity that you're going to use as a `From`, +`Source`, `Sender`, or `Return-Path` address. Verifying an identity with Amazon SES confirms that you own it and +helps prevent unauthorized use. + +To verify an identity for a hosted zone, you create an `EmailIdentity`: + +```ts +declare const myHostedZone: route53.IPublicHostedZone; + +const identity = new ses.EmailIdentity(stack, 'Identity', { + identity: ses.Identity.publicHostedZone(myHostedZone), + mailFromDomain: 'mail.cdk.dev', +}); +``` + +By default, [Easy DKIM](https://docs.aws.amazon.com/ses/latest/dg/send-email-authentication-dkim-easy.html) with +a 2048-bit DKIM key is used. + +You can instead configure DKIM authentication by using your own public-private key pair. This process is known +as [Bring Your Own DKIM (BYODKIM)](https://docs.aws.amazon.com/ses/latest/dg/send-email-authentication-dkim-bring-your-own.html): + +```ts +declare const myHostedZone: route53.IPublicHostedZone; + +new ses.EmailIdentity(stack, 'Identity', { + identity: ses.Identity.publicHostedZone(myHostedZone), + dkimIdentity: DkimIdentity.byoDkim({ + privateKey: SecretValue.secretsManager('dkim-private-key'), + publicKey: '...base64-encoded-public-key...', + selector: 'selector', + }), +}); +``` + +When using `publicHostedZone()` for the identity, all necessary Amazon Route 53 records are created automatically: + +* CNAME records for Easy DKIM +* TXT record for BYOD DKIM +* MX and TXT records for the custom MAIL FROM + +When working with `domain()`, records must be created manually: + +```ts +const identity = new ses.EmailIdentity(stack, 'Identity', { + identity: ses.Identity.domain('cdk.dev'), +}); + +for (const record of identity.dkimRecords) { + // create CNAME records using `record.name` and `record.value` +} +``` diff --git a/packages/@aws-cdk/aws-ses/lib/configuration-set.ts b/packages/@aws-cdk/aws-ses/lib/configuration-set.ts new file mode 100644 index 0000000000000..6e801a7f4f3f9 --- /dev/null +++ b/packages/@aws-cdk/aws-ses/lib/configuration-set.ts @@ -0,0 +1,171 @@ +import { IResource, Resource } from '@aws-cdk/core'; +import { Construct } from 'constructs'; +import { IDedicatedIpPool } from './dedicated-ip-pool'; +import { undefinedIfNoKeys } from './private/utils'; +import { CfnConfigurationSet } from './ses.generated'; + +/** + * A configuration set + */ +export interface IConfigurationSet extends IResource { + /** + * The name of the configuration set + * + * @attribute + */ + readonly configurationSetName: string; +} + +/** + * Properties for a configuration set + */ +export interface ConfigurationSetProps { + /** + * A name for the configuration set + * + * @default - a CloudFormation generated name + */ + readonly configurationSetName?: string; + + /** + * The dedicated IP pool to associate with the configuration set + * + * @default - do not use a dedicated IP pool + */ + readonly dedicatedIpPool?: IDedicatedIpPool; + + /** + * Specifies whether messages that use the configuration set are required to + * use Transport Layer Security (TLS) + * + * @default ConfigurationSetTlsPolicy.OPTIONAL + */ + readonly tlsPolicy?: ConfigurationSetTlsPolicy; + + /** + * Whether to publish reputation metrics for the configuration set, such as + * bounce and complaint rates, to Amazon CloudWatch + * + * @default false + */ + readonly reputationMetrics?: boolean; + + /** + * Whether email sending is enabled + * + * @default true + */ + readonly sendingEnabled?: boolean; + + /** + * The reasons for which recipient email addresses should be automatically added + * to your account's suppression list + * + * @default - use account level settings + */ + readonly suppressionReasons?: SuppressionReasons; + + /** + * The custom subdomain that is used to redirect email recipients to the + * Amazon SES event tracking domain + * + * @default - use the default awstrack.me domain + */ + readonly customTrackingRedirectDomain?: string, +} + +/** + * TLS policy for a configuration set + */ +export enum ConfigurationSetTlsPolicy { + /** + * Messages are only delivered if a TLS connection can be established + */ + REQUIRE = 'REQUIRE', + + /** + * Messages can be delivered in plain text if a TLS connection can't be established + */ + OPTIONAL = 'OPTIONAL', +} + +/** + * Reasons for which recipient email addresses should be automatically added + * to your account's suppression list + */ +export enum SuppressionReasons { + /** + * Bounces and complaints + */ + BOUNCES_AND_COMPLAINTS = 'BOUNCES_AND_COMPLAINTS', + + /** + * Bounces only + */ + BOUNCES_ONLY = 'BOUNCES_ONLY', + + /** + * Complaints only + */ + COMPLAINTS_ONLY = 'COMPLAINTS_ONLY', +} + +/** + * A configuration set + */ +export class ConfigurationSet extends Resource implements IConfigurationSet { + /** + * Use an existing configuration set + */ + public static fromConfigurationSetName(scope: Construct, id: string, configurationSetName: string): IConfigurationSet { + class Import extends Resource implements IConfigurationSet { + public readonly configurationSetName = configurationSetName; + } + return new Import(scope, id); + } + + public readonly configurationSetName: string; + + constructor(scope: Construct, id: string, props: ConfigurationSetProps = {}) { + super(scope, id, { + physicalName: props.configurationSetName, + }); + + const configurationSet = new CfnConfigurationSet(this, 'Resource', { + deliveryOptions: undefinedIfNoKeys({ + sendingPoolName: props.dedicatedIpPool?.dedicatedIpPoolName, + tlsPolicy: props.tlsPolicy, + }), + name: this.physicalName, + reputationOptions: undefinedIfNoKeys({ + reputationMetricsEnabled: props.reputationMetrics, + }), + sendingOptions: undefinedIfNoKeys({ + sendingEnabled: props.sendingEnabled, + }), + suppressionOptions: undefinedIfNoKeys({ + suppressedReasons: renderSuppressedReasons(props.suppressionReasons), + }), + trackingOptions: undefinedIfNoKeys({ + customRedirectDomain: props.customTrackingRedirectDomain, + }), + }); + + this.configurationSetName = configurationSet.ref; + } +} + +function renderSuppressedReasons(suppressionReasons?: SuppressionReasons): string[] | undefined { + if (!suppressionReasons) { + return undefined; + } + + switch (suppressionReasons) { + case SuppressionReasons.BOUNCES_AND_COMPLAINTS: + return ['BOUNCE', 'COMPLAINT']; + case SuppressionReasons.BOUNCES_ONLY: + return ['BOUNCE']; + case SuppressionReasons.COMPLAINTS_ONLY: + return ['COMPLAINT']; + } +} diff --git a/packages/@aws-cdk/aws-ses/lib/dedicated-ip-pool.ts b/packages/@aws-cdk/aws-ses/lib/dedicated-ip-pool.ts new file mode 100644 index 0000000000000..f650c45465376 --- /dev/null +++ b/packages/@aws-cdk/aws-ses/lib/dedicated-ip-pool.ts @@ -0,0 +1,56 @@ +import { IResource, Resource } from '@aws-cdk/core'; +import { Construct } from 'constructs'; +import { CfnDedicatedIpPool } from './ses.generated'; + +/** + * A dedicated IP pool + */ +export interface IDedicatedIpPool extends IResource { + /** + * The name of the dedicated IP pool + * + * @attribute + */ + readonly dedicatedIpPoolName: string +} + +/** + * Properties for a dedicated IP pool + */ +export interface DedicatedIpPoolProps { + /** + * A name for the dedicated IP pool + * + * @default - a CloudFormation generated name + */ + readonly dedicatedIpPoolName?: string; +} + +/** + * A dedicated IP pool + */ +export class DedicatedIpPool extends Resource implements IDedicatedIpPool { + /** + * Use an existing dedicated IP pool + */ + public static fromDedicatedIpPoolName(scope: Construct, id: string, dedicatedIpPoolName: string): IDedicatedIpPool { + class Import extends Resource implements IDedicatedIpPool { + public readonly dedicatedIpPoolName = dedicatedIpPoolName; + } + return new Import(scope, id); + } + + public readonly dedicatedIpPoolName: string; + + constructor(scope: Construct, id: string, props: DedicatedIpPoolProps = {}) { + super(scope, id, { + physicalName: props.dedicatedIpPoolName, + }); + + const pool = new CfnDedicatedIpPool(this, 'Resource', { + poolName: this.physicalName, + }); + + this.dedicatedIpPoolName = pool.ref; + } +} diff --git a/packages/@aws-cdk/aws-ses/lib/email-identity.ts b/packages/@aws-cdk/aws-ses/lib/email-identity.ts new file mode 100644 index 0000000000000..ad905f5a5ed5c --- /dev/null +++ b/packages/@aws-cdk/aws-ses/lib/email-identity.ts @@ -0,0 +1,444 @@ +import * as route53 from '@aws-cdk/aws-route53'; +import { IPublicHostedZone } from '@aws-cdk/aws-route53'; +import { IResource, Lazy, Resource, SecretValue, Stack } from '@aws-cdk/core'; +import { Construct } from 'constructs'; +import { IConfigurationSet } from './configuration-set'; +import { undefinedIfNoKeys } from './private/utils'; +import { CfnEmailIdentity } from './ses.generated'; + +/** + * An email identity + */ +export interface IEmailIdentity extends IResource { + /** + * The name of the email identity + * + * @attribute + */ + readonly emailIdentityName: string; +} + +/** + * Properties for an email identity + */ +export interface EmailIdentityProps { + /** + * The email address or domain to verify. + */ + readonly identity: Identity; + + /** + * The configuration set to associate with the email identity + * + * @default - do not use a specific configuration set + */ + readonly configurationSet?: IConfigurationSet; + + /** + * Whether the messages that are sent from the identity are signed using DKIM + * + * @default true + */ + readonly dkimSigning?: boolean; + + /** + * The type of DKIM identity to use + * + * @default - Easy DKIM with a key length of 2048-bit + */ + readonly dkimIdentity?: DkimIdentity; + + /** + * Whether to receive email notifications when bounce or complaint events occur. + * These notifications are sent to the address that you specified in the `Return-Path` + * header of the original email. + * + * You're required to have a method of tracking bounces and complaints. If you haven't set + * up another mechanism for receiving bounce or complaint notifications (for example, by + * setting up an event destination), you receive an email notification when these events + * occur (even if this setting is disabled). + * + * @default true + */ + readonly feedbackForwarding?: boolean; + + /** + * The custom MAIL FROM domain that you want the verified identity to use. The MAIL FROM domain + * must meet the following criteria: + * - It has to be a subdomain of the verified identity + * - It can't be used to receive email + * - It can't be used in a "From" address if the MAIL FROM domain is a destination for feedback + * forwarding emails + * + * @default - use amazonses.com + */ + readonly mailFromDomain?: string; + + /** + * The action to take if the required MX record for the MAIL FROM domain isn't + * found when you send an email + * + * @default MailFromBehaviorOnMxFailure.USE_DEFAULT_VALUE + */ + readonly mailFromBehaviorOnMxFailure?: MailFromBehaviorOnMxFailure; +} + +/** + * Identity + */ +export abstract class Identity { + /** + * Verify an email address + * + * To complete the verification process look for an email from + * no-reply-aws@amazon.com, open it and click the link. + */ + public static email(email: string): Identity { + return { value: email }; + } + + /** + * Verify a domain name + * + * DKIM records will have to be added manually to complete the verification + * process + */ + public static domain(domain: string): Identity { + return { value: domain }; + } + + /** + * Verify a public hosted zone + * + * DKIM and MAIL FROM records will be added automatically to the hosted + * zone + */ + public static publicHostedZone(hostedZone: IPublicHostedZone): Identity { + return { + value: hostedZone.zoneName, + hostedZone, + }; + } + + /** + * The value of the identity + */ + public abstract readonly value: string; + + /** + * The hosted zone associated with this identity + * + * @default - no hosted zone is associated and no records are created + */ + public abstract readonly hostedZone?: IPublicHostedZone; +} + +/** + * The action to take if the required MX record for the MAIL FROM domain isn't + * found + */ +export enum MailFromBehaviorOnMxFailure { + /** + * The mail is sent using amazonses.com as the MAIL FROM domain + */ + USE_DEFAULT_VALUE = 'USE_DEFAULT_VALUE', + + /** + * The Amazon SES API v2 returns a `MailFromDomainNotVerified` error and doesn't + * attempt to deliver the email + */ + REJECT_MESSAGE = 'REJECT_MESSAGE', +} + +/** + * Configuration for DKIM identity + */ +export interface DkimIdentityConfig { + /** + * A private key that's used to generate a DKIM signature + * + * @default - use Easy DKIM + */ + readonly domainSigningPrivateKey?: string; + + /** + * A string that's used to identify a public key in the DNS configuration for + * a domain + * + * @default - use Easy DKIM + */ + readonly domainSigningSelector?: string; + + /** + * The key length of the future DKIM key pair to be generated. This can be changed + * at most once per day. + * + * @default EasyDkimSigningKeyLength.RSA_2048_BIT + */ + readonly nextSigningKeyLength?: EasyDkimSigningKeyLength +} + +/** + * The identity to use for DKIM + */ +export abstract class DkimIdentity { + /** + * Easy DKIM + * + * @param signingKeyLength The length of the signing key. This can be changed at + * most once per day. + * + * @see https://docs.aws.amazon.com/ses/latest/dg/send-email-authentication-dkim-easy.html + */ + public static easyDkim(signingKeyLength?: EasyDkimSigningKeyLength): DkimIdentity { + return new EasyDkim(signingKeyLength); + } + + /** + * Bring Your Own DKIM + * + * @param options Options for BYO DKIM + * + * @see https://docs.aws.amazon.com/ses/latest/dg/send-email-authentication-dkim-bring-your-own.html + */ + public static byoDkim(options: ByoDkimOptions): DkimIdentity { + return new ByoDkim(options); + } + + /** + * Binds this DKIM identity to the email identity + */ + public abstract bind(emailIdentity: EmailIdentity, hostedZone?: route53.IPublicHostedZone): DkimIdentityConfig | undefined; +} + +class EasyDkim extends DkimIdentity { + constructor(private readonly signingKeyLength?: EasyDkimSigningKeyLength) { + super(); + } + + public bind(emailIdentity: EmailIdentity, hostedZone?: route53.IPublicHostedZone): DkimIdentityConfig | undefined { + if (hostedZone) { + new route53.CnameRecord(emailIdentity, 'DkimDnsToken1', { + zone: hostedZone, + recordName: Lazy.string({ produce: () => emailIdentity.dkimDnsTokenName1 }), + domainName: Lazy.string({ produce: () => emailIdentity.dkimDnsTokenValue1 }), + }); + + new route53.CnameRecord(hostedZone, 'DkimDnsToken2', { + zone: hostedZone, + recordName: Lazy.string({ produce: () => emailIdentity.dkimDnsTokenName2 }), + domainName: Lazy.string({ produce: () => emailIdentity.dkimDnsTokenValue2 }), + }); + + new route53.CnameRecord(hostedZone, 'DkimDnsToken3', { + zone: hostedZone, + recordName: Lazy.string({ produce: () => emailIdentity.dkimDnsTokenName3 }), + domainName: Lazy.string({ produce: () => emailIdentity.dkimDnsTokenValue3 }), + }); + } + + return this.signingKeyLength + ? { nextSigningKeyLength: this.signingKeyLength } + : undefined; + } +} + +/** + * Options for BYO DKIM + */ +export interface ByoDkimOptions { + /** + * The private key that's used to generate a DKIM signature + */ + readonly privateKey: SecretValue; + + /** + * A string that's used to identify a public key in the DNS configuration for + * a domain + */ + readonly selector: string; + + /** + * The public key. If specified, a TXT record with the public key is created. + * + * @default - the validation TXT record with the public key is not created + */ + readonly publicKey?: string; +} + +class ByoDkim extends DkimIdentity { + constructor(private readonly options: ByoDkimOptions) { + super(); + } + + public bind(emailIdentity: EmailIdentity, hostedZone?: route53.IPublicHostedZone): DkimIdentityConfig | undefined { + if (hostedZone && this.options.publicKey) { + new route53.TxtRecord(emailIdentity, 'DkimTxt', { + zone: hostedZone, + recordName: `${this.options.selector}._domainkey`, + values: [`p=${this.options.publicKey}`], + }); + } + + return { + domainSigningPrivateKey: this.options.privateKey.unsafeUnwrap(), // safe usage + domainSigningSelector: this.options.selector, + }; + } +} + +/** + * The signing key length for Easy DKIM + */ +export enum EasyDkimSigningKeyLength { + /** + * RSA 1024-bit + */ + RSA_1024_BIT = 'RSA_1024_BIT', + + /** + * RSA 2048-bit + */ + RSA_2048_BIT = 'RSA_2048_BIT' +} + +/** + * An email identity + */ +export class EmailIdentity extends Resource implements IEmailIdentity { + /** + * Use an existing email identity + */ + public static fromEmailIdentityName(scope: Construct, id: string, emailIdentityName: string): IEmailIdentity { + class Import extends Resource implements IEmailIdentity { + public readonly emailIdentityName = emailIdentityName; + } + return new Import(scope, id); + } + + public readonly emailIdentityName: string; + + /** + * The host name for the first token that you have to add to the + * DNS configurationfor your domain + * + * @attribute + */ + public readonly dkimDnsTokenName1: string; + + /** + * The host name for the second token that you have to add to the + * DNS configuration for your domain + * + * @attribute + */ + public readonly dkimDnsTokenName2: string; + + /** + * The host name for the third token that you have to add to the + * DNS configuration for your domain + * + * @attribute + */ + public readonly dkimDnsTokenName3: string; + + /** + * The record value for the first token that you have to add to the + * DNS configuration for your domain + * + * @attribute + */ + public readonly dkimDnsTokenValue1: string; + + /** + * The record value for the second token that you have to add to the + * DNS configuration for your domain + * + * @attribute + */ + public readonly dkimDnsTokenValue2: string; + + /** + * The record value for the third token that you have to add to the + * DNS configuration for your domain + * + * @attribute + */ + public readonly dkimDnsTokenValue3: string; + + /** + * DKIM records for this identity + */ + public readonly dkimRecords: DkimRecord[]; + + constructor(scope: Construct, id: string, props: EmailIdentityProps) { + super(scope, id); + + const dkimIdentity = props.dkimIdentity ?? DkimIdentity.easyDkim(); + + const identity = new CfnEmailIdentity(this, 'Resource', { + emailIdentity: props.identity.value, + configurationSetAttributes: undefinedIfNoKeys({ + configurationSetName: props.configurationSet?.configurationSetName, + }), + dkimAttributes: undefinedIfNoKeys({ + signingEnabled: props.dkimSigning, + }), + dkimSigningAttributes: dkimIdentity.bind(this, props.identity.hostedZone), + feedbackAttributes: undefinedIfNoKeys({ + emailForwardingEnabled: props.feedbackForwarding, + }), + mailFromAttributes: undefinedIfNoKeys({ + mailFromDomain: props.mailFromDomain, + behaviorOnMxFailure: props.mailFromBehaviorOnMxFailure, + }), + }); + + if (props.mailFromDomain && props.identity.hostedZone) { + new route53.MxRecord(this, 'MailFromMxRecord', { + zone: props.identity.hostedZone, + recordName: props.mailFromDomain, + values: [{ + priority: 10, + hostName: `feedback-smtp.${Stack.of(this).region}.amazonses.com`, + }], + }); + + new route53.TxtRecord(this, 'MailFromTxtRecord', { + zone: props.identity.hostedZone, + recordName: props.mailFromDomain, + values: ['v=spf1 include:amazonses.com ~all'], + }); + } + + this.emailIdentityName = identity.ref; + + this.dkimDnsTokenName1 = identity.attrDkimDnsTokenName1; + this.dkimDnsTokenName2 = identity.attrDkimDnsTokenName2; + this.dkimDnsTokenName3 = identity.attrDkimDnsTokenName3; + this.dkimDnsTokenValue1 = identity.attrDkimDnsTokenValue1; + this.dkimDnsTokenValue2 = identity.attrDkimDnsTokenValue2; + this.dkimDnsTokenValue3 = identity.attrDkimDnsTokenValue3; + + this.dkimRecords = [ + { name: this.dkimDnsTokenName1, value: this.dkimDnsTokenValue1 }, + { name: this.dkimDnsTokenName2, value: this.dkimDnsTokenValue2 }, + { name: this.dkimDnsTokenName3, value: this.dkimDnsTokenValue3 }, + ]; + } +} + +/** + * A DKIM record + */ +export interface DkimRecord { + /** + * The name of the record + */ + readonly name: string; + + /** + * The value of the record + */ + readonly value: string; +} diff --git a/packages/@aws-cdk/aws-ses/lib/index.ts b/packages/@aws-cdk/aws-ses/lib/index.ts index 078887862180c..94837f91a38ae 100644 --- a/packages/@aws-cdk/aws-ses/lib/index.ts +++ b/packages/@aws-cdk/aws-ses/lib/index.ts @@ -2,6 +2,9 @@ export * from './receipt-rule-set'; export * from './receipt-rule'; export * from './receipt-rule-action'; export * from './receipt-filter'; +export * from './dedicated-ip-pool'; +export * from './configuration-set'; +export * from './email-identity'; // AWS::SES CloudFormation Resources: export * from './ses.generated'; diff --git a/packages/@aws-cdk/aws-ses/lib/private/utils.ts b/packages/@aws-cdk/aws-ses/lib/private/utils.ts new file mode 100644 index 0000000000000..fd00f3bda7b77 --- /dev/null +++ b/packages/@aws-cdk/aws-ses/lib/private/utils.ts @@ -0,0 +1,4 @@ +export function undefinedIfNoKeys(obj: A): A | undefined { + const allUndefined = Object.values(obj).every(val => val === undefined); + return allUndefined ? undefined : obj; +} diff --git a/packages/@aws-cdk/aws-ses/package.json b/packages/@aws-cdk/aws-ses/package.json index ce52874575bc6..428330f4963a0 100644 --- a/packages/@aws-cdk/aws-ses/package.json +++ b/packages/@aws-cdk/aws-ses/package.json @@ -82,6 +82,7 @@ "devDependencies": { "@aws-cdk/assertions": "0.0.0", "@aws-cdk/cdk-build-tools": "0.0.0", + "@aws-cdk/integ-tests": "0.0.0", "@aws-cdk/integ-runner": "0.0.0", "@aws-cdk/cfn2ts": "0.0.0", "@aws-cdk/pkglint": "0.0.0", @@ -92,6 +93,7 @@ "dependencies": { "@aws-cdk/aws-iam": "0.0.0", "@aws-cdk/aws-lambda": "0.0.0", + "@aws-cdk/aws-route53": "0.0.0", "@aws-cdk/core": "0.0.0", "constructs": "^10.0.0" }, @@ -99,6 +101,7 @@ "peerDependencies": { "@aws-cdk/aws-iam": "0.0.0", "@aws-cdk/aws-lambda": "0.0.0", + "@aws-cdk/aws-route53": "0.0.0", "@aws-cdk/core": "0.0.0", "constructs": "^10.0.0" }, @@ -118,7 +121,8 @@ "docs-public-apis:@aws-cdk/aws-ses.DropSpamReceiptRuleProps", "props-default-doc:@aws-cdk/aws-ses.ReceiptRuleActionConfig.s3Action", "props-default-doc:@aws-cdk/aws-ses.ReceiptRuleActionConfig.bounceAction", - "props-default-doc:@aws-cdk/aws-ses.ReceiptRuleActionConfig.lambdaAction" + "props-default-doc:@aws-cdk/aws-ses.ReceiptRuleActionConfig.lambdaAction", + "props-physical-name:@aws-cdk/aws-ses.EmailIdentityProps" ] }, "awscdkio": { diff --git a/packages/@aws-cdk/aws-ses/rosetta/default.ts-fixture b/packages/@aws-cdk/aws-ses/rosetta/default.ts-fixture index f86757d5fe6f4..344e962cb852d 100644 --- a/packages/@aws-cdk/aws-ses/rosetta/default.ts-fixture +++ b/packages/@aws-cdk/aws-ses/rosetta/default.ts-fixture @@ -3,10 +3,11 @@ import { Stack } from '@aws-cdk/core'; import { Construct } from 'constructs'; import * as ses from '@aws-cdk/aws-ses'; import * as sns from '@aws-cdk/aws-sns'; +import * as route53 from '@aws-cdk/aws-route53'; class Fixture extends Stack { constructor(scope: Construct, id: string) { super(scope, id); /// here } -} \ No newline at end of file +} diff --git a/packages/@aws-cdk/aws-ses/test/configuration-set.integ.snapshot/ConfigurationSetIntegDefaultTestDeployAssert9B6AD46A.template.json b/packages/@aws-cdk/aws-ses/test/configuration-set.integ.snapshot/ConfigurationSetIntegDefaultTestDeployAssert9B6AD46A.template.json new file mode 100644 index 0000000000000..9e26dfeeb6e64 --- /dev/null +++ b/packages/@aws-cdk/aws-ses/test/configuration-set.integ.snapshot/ConfigurationSetIntegDefaultTestDeployAssert9B6AD46A.template.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-ses/test/configuration-set.integ.snapshot/cdk-ses-configuration-set-integ.template.json b/packages/@aws-cdk/aws-ses/test/configuration-set.integ.snapshot/cdk-ses-configuration-set-integ.template.json new file mode 100644 index 0000000000000..5a0081e6930f0 --- /dev/null +++ b/packages/@aws-cdk/aws-ses/test/configuration-set.integ.snapshot/cdk-ses-configuration-set-integ.template.json @@ -0,0 +1,7 @@ +{ + "Resources": { + "ConfigurationSet3DD38186": { + "Type": "AWS::SES::ConfigurationSet" + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-ses/test/configuration-set.integ.snapshot/cdk.out b/packages/@aws-cdk/aws-ses/test/configuration-set.integ.snapshot/cdk.out new file mode 100644 index 0000000000000..588d7b269d34f --- /dev/null +++ b/packages/@aws-cdk/aws-ses/test/configuration-set.integ.snapshot/cdk.out @@ -0,0 +1 @@ +{"version":"20.0.0"} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-ses/test/configuration-set.integ.snapshot/integ.json b/packages/@aws-cdk/aws-ses/test/configuration-set.integ.snapshot/integ.json new file mode 100644 index 0000000000000..ecc0733ebfbdf --- /dev/null +++ b/packages/@aws-cdk/aws-ses/test/configuration-set.integ.snapshot/integ.json @@ -0,0 +1,11 @@ +{ + "version": "20.0.0", + "testCases": { + "ConfigurationSetInteg/DefaultTest": { + "stacks": [ + "cdk-ses-configuration-set-integ" + ], + "assertionStack": "ConfigurationSetIntegDefaultTestDeployAssert9B6AD46A" + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-ses/test/configuration-set.integ.snapshot/manifest.json b/packages/@aws-cdk/aws-ses/test/configuration-set.integ.snapshot/manifest.json new file mode 100644 index 0000000000000..40ef7b019b59d --- /dev/null +++ b/packages/@aws-cdk/aws-ses/test/configuration-set.integ.snapshot/manifest.json @@ -0,0 +1,37 @@ +{ + "version": "20.0.0", + "artifacts": { + "Tree": { + "type": "cdk:tree", + "properties": { + "file": "tree.json" + } + }, + "cdk-ses-configuration-set-integ": { + "type": "aws:cloudformation:stack", + "environment": "aws://unknown-account/unknown-region", + "properties": { + "templateFile": "cdk-ses-configuration-set-integ.template.json", + "validateOnSynth": false + }, + "metadata": { + "/cdk-ses-configuration-set-integ/ConfigurationSet/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "ConfigurationSet3DD38186" + } + ] + }, + "displayName": "cdk-ses-configuration-set-integ" + }, + "ConfigurationSetIntegDefaultTestDeployAssert9B6AD46A": { + "type": "aws:cloudformation:stack", + "environment": "aws://unknown-account/unknown-region", + "properties": { + "templateFile": "ConfigurationSetIntegDefaultTestDeployAssert9B6AD46A.template.json", + "validateOnSynth": false + }, + "displayName": "ConfigurationSetInteg/DefaultTest/DeployAssert" + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-ses/test/configuration-set.integ.snapshot/tree.json b/packages/@aws-cdk/aws-ses/test/configuration-set.integ.snapshot/tree.json new file mode 100644 index 0000000000000..f5b731b191619 --- /dev/null +++ b/packages/@aws-cdk/aws-ses/test/configuration-set.integ.snapshot/tree.json @@ -0,0 +1,89 @@ +{ + "version": "tree-0.1", + "tree": { + "id": "App", + "path": "", + "children": { + "Tree": { + "id": "Tree", + "path": "Tree", + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.1.33" + } + }, + "cdk-ses-configuration-set-integ": { + "id": "cdk-ses-configuration-set-integ", + "path": "cdk-ses-configuration-set-integ", + "children": { + "ConfigurationSet": { + "id": "ConfigurationSet", + "path": "cdk-ses-configuration-set-integ/ConfigurationSet", + "children": { + "Resource": { + "id": "Resource", + "path": "cdk-ses-configuration-set-integ/ConfigurationSet/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::SES::ConfigurationSet", + "aws:cdk:cloudformation:props": {} + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-ses.CfnConfigurationSet", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-ses.ConfigurationSet", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/core.Stack", + "version": "0.0.0" + } + }, + "ConfigurationSetInteg": { + "id": "ConfigurationSetInteg", + "path": "ConfigurationSetInteg", + "children": { + "DefaultTest": { + "id": "DefaultTest", + "path": "ConfigurationSetInteg/DefaultTest", + "children": { + "Default": { + "id": "Default", + "path": "ConfigurationSetInteg/DefaultTest/Default", + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.1.33" + } + }, + "DeployAssert": { + "id": "DeployAssert", + "path": "ConfigurationSetInteg/DefaultTest/DeployAssert", + "constructInfo": { + "fqn": "@aws-cdk/core.Stack", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/integ-tests.IntegTestCase", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/integ-tests.IntegTest", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/core.App", + "version": "0.0.0" + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-ses/test/configuration-set.test.ts b/packages/@aws-cdk/aws-ses/test/configuration-set.test.ts new file mode 100644 index 0000000000000..9a9fb8ebb0dc9 --- /dev/null +++ b/packages/@aws-cdk/aws-ses/test/configuration-set.test.ts @@ -0,0 +1,42 @@ +import { Template, Match } from '@aws-cdk/assertions'; +import { Stack } from '@aws-cdk/core'; +import { ConfigurationSet, ConfigurationSetTlsPolicy, DedicatedIpPool, SuppressionReasons } from '../lib'; + +let stack: Stack; +beforeEach(() => { + stack = new Stack(); +}); + +test('default configuration set', () => { + new ConfigurationSet(stack, 'ConfigurationSet'); + + Template.fromStack(stack).hasResourceProperties('AWS::SES::ConfigurationSet', { + Name: Match.absent(), + }); +}); + +test('configuration set with options', () => { + new ConfigurationSet(stack, 'ConfigurationSet', { + customTrackingRedirectDomain: 'track.cdk.dev', + suppressionReasons: SuppressionReasons.COMPLAINTS_ONLY, + tlsPolicy: ConfigurationSetTlsPolicy.REQUIRE, + dedicatedIpPool: new DedicatedIpPool(stack, 'Pool'), + }); + + Template.fromStack(stack).hasResourceProperties('AWS::SES::ConfigurationSet', { + DeliveryOptions: { + SendingPoolName: { + Ref: 'PoolD3F588B8', + }, + TlsPolicy: 'REQUIRE', + }, + SuppressionOptions: { + SuppressedReasons: [ + 'COMPLAINT', + ], + }, + TrackingOptions: { + CustomRedirectDomain: 'track.cdk.dev', + }, + }); +}); diff --git a/packages/@aws-cdk/aws-ses/test/dedicated-ip-pool.test.ts b/packages/@aws-cdk/aws-ses/test/dedicated-ip-pool.test.ts new file mode 100644 index 0000000000000..c635da3790319 --- /dev/null +++ b/packages/@aws-cdk/aws-ses/test/dedicated-ip-pool.test.ts @@ -0,0 +1,17 @@ +import { Template, Match } from '@aws-cdk/assertions'; +import { Stack } from '@aws-cdk/core'; +import { DedicatedIpPool } from '../lib'; + +let stack: Stack; +beforeEach(() => { + stack = new Stack(); +}); + +test('default dedicated IP pool', () => { + // GIVEN + new DedicatedIpPool(stack, 'Pool'); + + Template.fromStack(stack).hasResourceProperties('AWS::SES::DedicatedIpPool', { + PoolName: Match.absent(), + }); +}); diff --git a/packages/@aws-cdk/aws-ses/test/email-identity.integ.snapshot/EmailIdentityIntegDefaultTestDeployAssert3F909307.template.json b/packages/@aws-cdk/aws-ses/test/email-identity.integ.snapshot/EmailIdentityIntegDefaultTestDeployAssert3F909307.template.json new file mode 100644 index 0000000000000..9e26dfeeb6e64 --- /dev/null +++ b/packages/@aws-cdk/aws-ses/test/email-identity.integ.snapshot/EmailIdentityIntegDefaultTestDeployAssert3F909307.template.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-ses/test/email-identity.integ.snapshot/cdk-ses-email-identity-integ.template.json b/packages/@aws-cdk/aws-ses/test/email-identity.integ.snapshot/cdk-ses-email-identity-integ.template.json new file mode 100644 index 0000000000000..6f3239479a0e6 --- /dev/null +++ b/packages/@aws-cdk/aws-ses/test/email-identity.integ.snapshot/cdk-ses-email-identity-integ.template.json @@ -0,0 +1,154 @@ +{ + "Resources": { + "HostedZoneDB99F866": { + "Type": "AWS::Route53::HostedZone", + "Properties": { + "Name": "cdk.dev." + } + }, + "HostedZoneDkimDnsToken2A07BC1AD": { + "Type": "AWS::Route53::RecordSet", + "Properties": { + "Name": { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "EmailIdentity7187767D", + "DkimDNSTokenName2" + ] + }, + ".cdk.dev." + ] + ] + }, + "Type": "CNAME", + "HostedZoneId": { + "Ref": "HostedZoneDB99F866" + }, + "ResourceRecords": [ + { + "Fn::GetAtt": [ + "EmailIdentity7187767D", + "DkimDNSTokenValue2" + ] + } + ], + "TTL": "1800" + } + }, + "HostedZoneDkimDnsToken34DA6A373": { + "Type": "AWS::Route53::RecordSet", + "Properties": { + "Name": { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "EmailIdentity7187767D", + "DkimDNSTokenName3" + ] + }, + ".cdk.dev." + ] + ] + }, + "Type": "CNAME", + "HostedZoneId": { + "Ref": "HostedZoneDB99F866" + }, + "ResourceRecords": [ + { + "Fn::GetAtt": [ + "EmailIdentity7187767D", + "DkimDNSTokenValue3" + ] + } + ], + "TTL": "1800" + } + }, + "EmailIdentityDkimDnsToken15F8EC787": { + "Type": "AWS::Route53::RecordSet", + "Properties": { + "Name": { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "EmailIdentity7187767D", + "DkimDNSTokenName1" + ] + }, + ".cdk.dev." + ] + ] + }, + "Type": "CNAME", + "HostedZoneId": { + "Ref": "HostedZoneDB99F866" + }, + "ResourceRecords": [ + { + "Fn::GetAtt": [ + "EmailIdentity7187767D", + "DkimDNSTokenValue1" + ] + } + ], + "TTL": "1800" + } + }, + "EmailIdentity7187767D": { + "Type": "AWS::SES::EmailIdentity", + "Properties": { + "EmailIdentity": "cdk.dev", + "MailFromAttributes": { + "MailFromDomain": "mail.cdk.dev" + } + } + }, + "EmailIdentityMailFromMxRecordCEAAECD0": { + "Type": "AWS::Route53::RecordSet", + "Properties": { + "Name": "mail.cdk.dev.", + "Type": "MX", + "HostedZoneId": { + "Ref": "HostedZoneDB99F866" + }, + "ResourceRecords": [ + { + "Fn::Join": [ + "", + [ + "10 feedback-smtp.", + { + "Ref": "AWS::Region" + }, + ".amazonses.com" + ] + ] + } + ], + "TTL": "1800" + } + }, + "EmailIdentityMailFromTxtRecordE6B5E5D0": { + "Type": "AWS::Route53::RecordSet", + "Properties": { + "Name": "mail.cdk.dev.", + "Type": "TXT", + "HostedZoneId": { + "Ref": "HostedZoneDB99F866" + }, + "ResourceRecords": [ + "\"v=spf1 include:amazonses.com ~all\"" + ], + "TTL": "1800" + } + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-ses/test/email-identity.integ.snapshot/cdk.out b/packages/@aws-cdk/aws-ses/test/email-identity.integ.snapshot/cdk.out new file mode 100644 index 0000000000000..588d7b269d34f --- /dev/null +++ b/packages/@aws-cdk/aws-ses/test/email-identity.integ.snapshot/cdk.out @@ -0,0 +1 @@ +{"version":"20.0.0"} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-ses/test/email-identity.integ.snapshot/integ.json b/packages/@aws-cdk/aws-ses/test/email-identity.integ.snapshot/integ.json new file mode 100644 index 0000000000000..c8f2061eef610 --- /dev/null +++ b/packages/@aws-cdk/aws-ses/test/email-identity.integ.snapshot/integ.json @@ -0,0 +1,11 @@ +{ + "version": "20.0.0", + "testCases": { + "EmailIdentityInteg/DefaultTest": { + "stacks": [ + "cdk-ses-email-identity-integ" + ], + "assertionStack": "EmailIdentityIntegDefaultTestDeployAssert3F909307" + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-ses/test/email-identity.integ.snapshot/manifest.json b/packages/@aws-cdk/aws-ses/test/email-identity.integ.snapshot/manifest.json new file mode 100644 index 0000000000000..787ac36f4d175 --- /dev/null +++ b/packages/@aws-cdk/aws-ses/test/email-identity.integ.snapshot/manifest.json @@ -0,0 +1,73 @@ +{ + "version": "20.0.0", + "artifacts": { + "Tree": { + "type": "cdk:tree", + "properties": { + "file": "tree.json" + } + }, + "cdk-ses-email-identity-integ": { + "type": "aws:cloudformation:stack", + "environment": "aws://unknown-account/unknown-region", + "properties": { + "templateFile": "cdk-ses-email-identity-integ.template.json", + "validateOnSynth": false + }, + "metadata": { + "/cdk-ses-email-identity-integ/HostedZone/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "HostedZoneDB99F866" + } + ], + "/cdk-ses-email-identity-integ/HostedZone/DkimDnsToken2/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "HostedZoneDkimDnsToken2A07BC1AD" + } + ], + "/cdk-ses-email-identity-integ/HostedZone/DkimDnsToken3/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "HostedZoneDkimDnsToken34DA6A373" + } + ], + "/cdk-ses-email-identity-integ/EmailIdentity/DkimDnsToken1/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "EmailIdentityDkimDnsToken15F8EC787" + } + ], + "/cdk-ses-email-identity-integ/EmailIdentity/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "EmailIdentity7187767D" + } + ], + "/cdk-ses-email-identity-integ/EmailIdentity/MailFromMxRecord/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "EmailIdentityMailFromMxRecordCEAAECD0" + } + ], + "/cdk-ses-email-identity-integ/EmailIdentity/MailFromTxtRecord/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "EmailIdentityMailFromTxtRecordE6B5E5D0" + } + ] + }, + "displayName": "cdk-ses-email-identity-integ" + }, + "EmailIdentityIntegDefaultTestDeployAssert3F909307": { + "type": "aws:cloudformation:stack", + "environment": "aws://unknown-account/unknown-region", + "properties": { + "templateFile": "EmailIdentityIntegDefaultTestDeployAssert3F909307.template.json", + "validateOnSynth": false + }, + "displayName": "EmailIdentityInteg/DefaultTest/DeployAssert" + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-ses/test/email-identity.integ.snapshot/tree.json b/packages/@aws-cdk/aws-ses/test/email-identity.integ.snapshot/tree.json new file mode 100644 index 0000000000000..477ac6f768970 --- /dev/null +++ b/packages/@aws-cdk/aws-ses/test/email-identity.integ.snapshot/tree.json @@ -0,0 +1,343 @@ +{ + "version": "tree-0.1", + "tree": { + "id": "App", + "path": "", + "children": { + "Tree": { + "id": "Tree", + "path": "Tree", + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.1.33" + } + }, + "cdk-ses-email-identity-integ": { + "id": "cdk-ses-email-identity-integ", + "path": "cdk-ses-email-identity-integ", + "children": { + "HostedZone": { + "id": "HostedZone", + "path": "cdk-ses-email-identity-integ/HostedZone", + "children": { + "Resource": { + "id": "Resource", + "path": "cdk-ses-email-identity-integ/HostedZone/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::Route53::HostedZone", + "aws:cdk:cloudformation:props": { + "name": "cdk.dev." + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-route53.CfnHostedZone", + "version": "0.0.0" + } + }, + "DkimDnsToken2": { + "id": "DkimDnsToken2", + "path": "cdk-ses-email-identity-integ/HostedZone/DkimDnsToken2", + "children": { + "Resource": { + "id": "Resource", + "path": "cdk-ses-email-identity-integ/HostedZone/DkimDnsToken2/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::Route53::RecordSet", + "aws:cdk:cloudformation:props": { + "name": { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "EmailIdentity7187767D", + "DkimDNSTokenName2" + ] + }, + ".cdk.dev." + ] + ] + }, + "type": "CNAME", + "hostedZoneId": { + "Ref": "HostedZoneDB99F866" + }, + "resourceRecords": [ + { + "Fn::GetAtt": [ + "EmailIdentity7187767D", + "DkimDNSTokenValue2" + ] + } + ], + "ttl": "1800" + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-route53.CfnRecordSet", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-route53.CnameRecord", + "version": "0.0.0" + } + }, + "DkimDnsToken3": { + "id": "DkimDnsToken3", + "path": "cdk-ses-email-identity-integ/HostedZone/DkimDnsToken3", + "children": { + "Resource": { + "id": "Resource", + "path": "cdk-ses-email-identity-integ/HostedZone/DkimDnsToken3/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::Route53::RecordSet", + "aws:cdk:cloudformation:props": { + "name": { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "EmailIdentity7187767D", + "DkimDNSTokenName3" + ] + }, + ".cdk.dev." + ] + ] + }, + "type": "CNAME", + "hostedZoneId": { + "Ref": "HostedZoneDB99F866" + }, + "resourceRecords": [ + { + "Fn::GetAtt": [ + "EmailIdentity7187767D", + "DkimDNSTokenValue3" + ] + } + ], + "ttl": "1800" + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-route53.CfnRecordSet", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-route53.CnameRecord", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-route53.PublicHostedZone", + "version": "0.0.0" + } + }, + "EmailIdentity": { + "id": "EmailIdentity", + "path": "cdk-ses-email-identity-integ/EmailIdentity", + "children": { + "DkimDnsToken1": { + "id": "DkimDnsToken1", + "path": "cdk-ses-email-identity-integ/EmailIdentity/DkimDnsToken1", + "children": { + "Resource": { + "id": "Resource", + "path": "cdk-ses-email-identity-integ/EmailIdentity/DkimDnsToken1/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::Route53::RecordSet", + "aws:cdk:cloudformation:props": { + "name": { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "EmailIdentity7187767D", + "DkimDNSTokenName1" + ] + }, + ".cdk.dev." + ] + ] + }, + "type": "CNAME", + "hostedZoneId": { + "Ref": "HostedZoneDB99F866" + }, + "resourceRecords": [ + { + "Fn::GetAtt": [ + "EmailIdentity7187767D", + "DkimDNSTokenValue1" + ] + } + ], + "ttl": "1800" + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-route53.CfnRecordSet", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-route53.CnameRecord", + "version": "0.0.0" + } + }, + "Resource": { + "id": "Resource", + "path": "cdk-ses-email-identity-integ/EmailIdentity/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::SES::EmailIdentity", + "aws:cdk:cloudformation:props": { + "emailIdentity": "cdk.dev", + "mailFromAttributes": { + "mailFromDomain": "mail.cdk.dev" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-ses.CfnEmailIdentity", + "version": "0.0.0" + } + }, + "MailFromMxRecord": { + "id": "MailFromMxRecord", + "path": "cdk-ses-email-identity-integ/EmailIdentity/MailFromMxRecord", + "children": { + "Resource": { + "id": "Resource", + "path": "cdk-ses-email-identity-integ/EmailIdentity/MailFromMxRecord/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::Route53::RecordSet", + "aws:cdk:cloudformation:props": { + "name": "mail.cdk.dev.", + "type": "MX", + "hostedZoneId": { + "Ref": "HostedZoneDB99F866" + }, + "resourceRecords": [ + { + "Fn::Join": [ + "", + [ + "10 feedback-smtp.", + { + "Ref": "AWS::Region" + }, + ".amazonses.com" + ] + ] + } + ], + "ttl": "1800" + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-route53.CfnRecordSet", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-route53.MxRecord", + "version": "0.0.0" + } + }, + "MailFromTxtRecord": { + "id": "MailFromTxtRecord", + "path": "cdk-ses-email-identity-integ/EmailIdentity/MailFromTxtRecord", + "children": { + "Resource": { + "id": "Resource", + "path": "cdk-ses-email-identity-integ/EmailIdentity/MailFromTxtRecord/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::Route53::RecordSet", + "aws:cdk:cloudformation:props": { + "name": "mail.cdk.dev.", + "type": "TXT", + "hostedZoneId": { + "Ref": "HostedZoneDB99F866" + }, + "resourceRecords": [ + "\"v=spf1 include:amazonses.com ~all\"" + ], + "ttl": "1800" + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-route53.CfnRecordSet", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-route53.TxtRecord", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-ses.EmailIdentity", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/core.Stack", + "version": "0.0.0" + } + }, + "EmailIdentityInteg": { + "id": "EmailIdentityInteg", + "path": "EmailIdentityInteg", + "children": { + "DefaultTest": { + "id": "DefaultTest", + "path": "EmailIdentityInteg/DefaultTest", + "children": { + "Default": { + "id": "Default", + "path": "EmailIdentityInteg/DefaultTest/Default", + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.1.33" + } + }, + "DeployAssert": { + "id": "DeployAssert", + "path": "EmailIdentityInteg/DefaultTest/DeployAssert", + "constructInfo": { + "fqn": "@aws-cdk/core.Stack", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/integ-tests.IntegTestCase", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/integ-tests.IntegTest", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/core.App", + "version": "0.0.0" + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-ses/test/email-identity.test.ts b/packages/@aws-cdk/aws-ses/test/email-identity.test.ts new file mode 100644 index 0000000000000..1b15e9c3dbed2 --- /dev/null +++ b/packages/@aws-cdk/aws-ses/test/email-identity.test.ts @@ -0,0 +1,217 @@ +import { Template } from '@aws-cdk/assertions'; +import * as route53 from '@aws-cdk/aws-route53'; +import { SecretValue, Stack } from '@aws-cdk/core'; +import { ConfigurationSet, DkimIdentity, EmailIdentity, Identity, MailFromBehaviorOnMxFailure } from '../lib'; + +let stack: Stack; +beforeEach(() => { + stack = new Stack(); +}); + +test('default email identity for a domain', () => { + new EmailIdentity(stack, 'Identity', { + identity: Identity.domain('cdk.dev'), + }); + + Template.fromStack(stack).hasResourceProperties('AWS::SES::EmailIdentity', { + EmailIdentity: 'cdk.dev', + }); +}); + +test('email identity from a hosted zone with easy dkim', () => { + const hostedZone = new route53.PublicHostedZone(stack, 'HostedZone', { + zoneName: 'cdk.dev', + }); + + new EmailIdentity(stack, 'Identity', { + identity: Identity.publicHostedZone(hostedZone), + configurationSet: new ConfigurationSet(stack, 'ConfigurationSet'), + }); + + Template.fromStack(stack).hasResourceProperties('AWS::SES::EmailIdentity', { + EmailIdentity: 'cdk.dev', + ConfigurationSetAttributes: { + ConfigurationSetName: { + Ref: 'ConfigurationSet3DD38186', + }, + }, + }); + + Template.fromStack(stack).resourceCountIs('AWS::Route53::RecordSet', 3); + + Template.fromStack(stack).hasResourceProperties('AWS::Route53::RecordSet', { + Name: { + 'Fn::Join': [ + '', + [ + { + 'Fn::GetAtt': [ + 'Identity2D60E2CC', + 'DkimDNSTokenName1', + ], + }, + '.cdk.dev.', + ], + ], + }, + Type: 'CNAME', + HostedZoneId: { + Ref: 'HostedZoneDB99F866', + }, + ResourceRecords: [ + { + 'Fn::GetAtt': [ + 'Identity2D60E2CC', + 'DkimDNSTokenValue1', + ], + }, + ], + }); + + Template.fromStack(stack).hasResourceProperties('AWS::Route53::RecordSet', { + Name: { + 'Fn::Join': [ + '', + [ + { + 'Fn::GetAtt': [ + 'Identity2D60E2CC', + 'DkimDNSTokenName2', + ], + }, + '.cdk.dev.', + ], + ], + }, + Type: 'CNAME', + HostedZoneId: { + Ref: 'HostedZoneDB99F866', + }, + ResourceRecords: [ + { + 'Fn::GetAtt': [ + 'Identity2D60E2CC', + 'DkimDNSTokenValue2', + ], + }, + ], + }); + + Template.fromStack(stack).hasResourceProperties('AWS::Route53::RecordSet', { + Name: { + 'Fn::Join': [ + '', + [ + { + 'Fn::GetAtt': [ + 'Identity2D60E2CC', + 'DkimDNSTokenName3', + ], + }, + '.cdk.dev.', + ], + ], + }, + Type: 'CNAME', + HostedZoneId: { + Ref: 'HostedZoneDB99F866', + }, + ResourceRecords: [ + { + 'Fn::GetAtt': [ + 'Identity2D60E2CC', + 'DkimDNSTokenValue3', + ], + }, + ], + }); +}); + +test('email identity from a hosted zone with BYO dkim', () => { + const hostedZone = new route53.PublicHostedZone(stack, 'HostedZone', { + zoneName: 'cdk.dev', + }); + + new EmailIdentity(stack, 'Identity', { + identity: Identity.publicHostedZone(hostedZone), + dkimIdentity: DkimIdentity.byoDkim({ + privateKey: SecretValue.secretsManager('my-secret'), + selector: 'selector', + publicKey: 'public-key', + }), + }); + + Template.fromStack(stack).hasResourceProperties('AWS::SES::EmailIdentity', { + EmailIdentity: 'cdk.dev', + DkimSigningAttributes: { + DomainSigningPrivateKey: '{{resolve:secretsmanager:my-secret:SecretString:::}}', + DomainSigningSelector: 'selector', + }, + }); + + Template.fromStack(stack).resourceCountIs('AWS::Route53::RecordSet', 1); + + Template.fromStack(stack).hasResourceProperties('AWS::Route53::RecordSet', { + Name: 'selector._domainkey.cdk.dev.', + Type: 'TXT', + HostedZoneId: { + Ref: 'HostedZoneDB99F866', + }, + ResourceRecords: [ + '"p=public-key"', + ], + }); +}); + +test('with mail from and hosted zone', () => { + const hostedZone = new route53.PublicHostedZone(stack, 'HostedZone', { + zoneName: 'cdk.dev', + }); + + new EmailIdentity(stack, 'Identity', { + identity: Identity.publicHostedZone(hostedZone), + mailFromDomain: 'mail.cdk.dev', + mailFromBehaviorOnMxFailure: MailFromBehaviorOnMxFailure.REJECT_MESSAGE, + }); + + Template.fromStack(stack).hasResourceProperties('AWS::SES::EmailIdentity', { + MailFromAttributes: { + BehaviorOnMxFailure: 'REJECT_MESSAGE', + MailFromDomain: 'mail.cdk.dev', + }, + }); + + Template.fromStack(stack).hasResourceProperties('AWS::Route53::RecordSet', { + Name: 'mail.cdk.dev.', + Type: 'MX', + HostedZoneId: { + Ref: 'HostedZoneDB99F866', + }, + ResourceRecords: [ + { + 'Fn::Join': [ + '', + [ + '10 feedback-smtp.', + { + Ref: 'AWS::Region', + }, + '.amazonses.com', + ], + ], + }, + ], + }); + + Template.fromStack(stack).hasResourceProperties('AWS::Route53::RecordSet', { + Name: 'mail.cdk.dev.', + Type: 'TXT', + HostedZoneId: { + Ref: 'HostedZoneDB99F866', + }, + ResourceRecords: [ + '"v=spf1 include:amazonses.com ~all"', + ], + }); +}); + diff --git a/packages/@aws-cdk/aws-ses/test/integ.configuration-set.ts b/packages/@aws-cdk/aws-ses/test/integ.configuration-set.ts new file mode 100644 index 0000000000000..ad13af5ea47eb --- /dev/null +++ b/packages/@aws-cdk/aws-ses/test/integ.configuration-set.ts @@ -0,0 +1,20 @@ +import { App, Stack, StackProps } from '@aws-cdk/core'; +import * as integ from '@aws-cdk/integ-tests'; +import { Construct } from 'constructs'; +import * as ses from '../lib'; + +class TestStack extends Stack { + constructor(scope: Construct, id: string, props?: StackProps) { + super(scope, id, props); + + new ses.ConfigurationSet(this, 'ConfigurationSet'); + } +} + +const app = new App(); + +new integ.IntegTest(app, 'ConfigurationSetInteg', { + testCases: [new TestStack(app, 'cdk-ses-configuration-set-integ')], +}); + +app.synth(); diff --git a/packages/@aws-cdk/aws-ses/test/integ.email-identity.ts b/packages/@aws-cdk/aws-ses/test/integ.email-identity.ts new file mode 100644 index 0000000000000..0e82eaf575642 --- /dev/null +++ b/packages/@aws-cdk/aws-ses/test/integ.email-identity.ts @@ -0,0 +1,28 @@ +import { PublicHostedZone } from '@aws-cdk/aws-route53'; +import { App, Stack, StackProps } from '@aws-cdk/core'; +import * as integ from '@aws-cdk/integ-tests'; +import { Construct } from 'constructs'; +import * as ses from '../lib'; + +class TestStack extends Stack { + constructor(scope: Construct, id: string, props?: StackProps) { + super(scope, id, props); + + const hostedZone = new PublicHostedZone(this, 'HostedZone', { + zoneName: 'cdk.dev', + }); + + new ses.EmailIdentity(this, 'EmailIdentity', { + identity: ses.Identity.publicHostedZone(hostedZone), + mailFromDomain: 'mail.cdk.dev', + }); + } +} + +const app = new App(); + +new integ.IntegTest(app, 'EmailIdentityInteg', { + testCases: [new TestStack(app, 'cdk-ses-email-identity-integ')], +}); + +app.synth(); diff --git a/packages/@aws-cdk/custom-resources/README.md b/packages/@aws-cdk/custom-resources/README.md index 1257486e4d8b7..db40ad6904544 100644 --- a/packages/@aws-cdk/custom-resources/README.md +++ b/packages/@aws-cdk/custom-resources/README.md @@ -547,33 +547,6 @@ path in `PhysicalResourceId.fromResponse()`. ### Custom Resource Examples -#### Verify a domain with SES - -```ts -import * as route53 from '@aws-cdk/aws-route53'; - -const verifyDomainIdentity = new cr.AwsCustomResource(this, 'VerifyDomainIdentity', { - onCreate: { - service: 'SES', - action: 'verifyDomainIdentity', - parameters: { - Domain: 'example.com', - }, - physicalResourceId: cr.PhysicalResourceId.fromResponse('VerificationToken'), // Use the token returned by the call as physical id - }, - policy: cr.AwsCustomResourcePolicy.fromSdkCalls({ - resources: cr.AwsCustomResourcePolicy.ANY_RESOURCE, - }), -}); - -declare const zone: route53.HostedZone; -new route53.TxtRecord(this, 'SESVerificationRecord', { - zone, - recordName: `_amazonses.example.com`, - values: [verifyDomainIdentity.getResponseField('VerificationToken')], -}); -``` - #### Get the latest version of a secure SSM parameter ```ts From 5f0eff291b2cac6f2fbddfbe84d06f3a92f70c1d Mon Sep 17 00:00:00 2001 From: Cory Hall <43035978+corymhall@users.noreply.github.com> Date: Wed, 13 Jul 2022 13:30:05 -0400 Subject: [PATCH 30/92] feat(secretsmanager): create secret with secretObjectValue (#21091) A common use case is to create key/value secrets where the values could be either strings _or_ other secret values. Currently this is possible, but the user experience is not great. This PR introduces a new input prop `secretObjectValue` which is of type `{ [key: string]: SecretValue }`. For example, you can now create a JSON secret: ```ts new secretsmanager.Secret(stack, 'JSONSecret', { secretObjectValue: { username: SecretValue.unsafePlainText(user.userName), // intrinsic reference, not exposed as plaintext database: SecretValue.unsafePlainText('foo'), // rendered as plain text, but not a secret password: accessKey.secretAccessKey, // SecretValue }, }); ``` I've also updated the docs to better reflect what `unsafe` means given this new context. fixes #20461 ---- ### All Submissions: * [x] Have you followed the guidelines in our [Contributing guide?](https://github.com/aws/aws-cdk/blob/main/CONTRIBUTING.md) ### Adding new Unconventional Dependencies: * [ ] This PR adds new unconventional dependencies following the process described [here](https://github.com/aws/aws-cdk/blob/main/CONTRIBUTING.md/#adding-new-unconventional-dependencies) ### New Features * [x] Have you added the new feature to an [integration test](https://github.com/aws/aws-cdk/blob/main/INTEGRATION_TESTS.md)? * [x] Did you use `yarn integ` to deploy the infrastructure and generate the snapshot (i.e. `yarn integ` without `--dry-run`)? *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- .../@aws-cdk/aws-secretsmanager/README.md | 33 ++++++++ .../@aws-cdk/aws-secretsmanager/lib/secret.ts | 58 ++++++++++++-- .../rosetta/default.ts-fixture | 4 +- .../test/integ.secret.lit.ts | 11 +++ .../Integ-SecretsManager-Secret.assets.json | 19 ----- .../Integ-SecretsManager-Secret.template.json | 26 ++++++ ...aultTestDeployAssert519F6A06.template.json | 1 + .../test/secret.lit.integ.snapshot/cdk.out | 2 +- .../test/secret.lit.integ.snapshot/integ.json | 11 +-- .../secret.lit.integ.snapshot/manifest.json | 17 +++- .../test/secret.lit.integ.snapshot/tree.json | 80 ++++++++++++++++++- .../aws-secretsmanager/test/secret.test.ts | 54 +++++++++++++ packages/@aws-cdk/core/lib/secret-value.ts | 15 +++- 13 files changed, 291 insertions(+), 40 deletions(-) delete mode 100644 packages/@aws-cdk/aws-secretsmanager/test/secret.lit.integ.snapshot/Integ-SecretsManager-Secret.assets.json create mode 100644 packages/@aws-cdk/aws-secretsmanager/test/secret.lit.integ.snapshot/SecretTestDefaultTestDeployAssert519F6A06.template.json diff --git a/packages/@aws-cdk/aws-secretsmanager/README.md b/packages/@aws-cdk/aws-secretsmanager/README.md index 780bdc8e022e6..3b99e340cf970 100644 --- a/packages/@aws-cdk/aws-secretsmanager/README.md +++ b/packages/@aws-cdk/aws-secretsmanager/README.md @@ -252,3 +252,36 @@ Alternatively, use `addReplicaRegion()`: const secret = new secretsmanager.Secret(this, 'Secret'); secret.addReplicaRegion('eu-west-1'); ``` + +## Creating JSON Secrets + +Sometimes it is necessary to create a secret in SecretsManager that contains a JSON object. +For example: + +```json +{ + "username": "myUsername", + "database": "foo", + "password": "mypassword" +} +``` + +In order to create this type of secret, use the `secretObjectValue` input prop. + +```ts +const user = new iam.User(stack, 'User'); +const accessKey = new iam.AccessKey(stack, 'AccessKey', { user }); +declare const stack: Stack; + +new secretsmanager.Secret(stack, 'Secret', { + secretObjectValue: { + username: SecretValue.unsafePlainText(user.userName), + database: SecretValue.unsafePlainText('foo'), + password: accessKey.secretAccessKey, + }, +}) +``` + +In this case both the `username` and `database` are not a `Secret` so `SecretValue.unsafePlainText` needs to be used. +This means that they will be rendered as plain text in the template, but in this case neither of those +are actual "secrets". diff --git a/packages/@aws-cdk/aws-secretsmanager/lib/secret.ts b/packages/@aws-cdk/aws-secretsmanager/lib/secret.ts index 7865b29c2c4bd..c7426329f61ce 100644 --- a/packages/@aws-cdk/aws-secretsmanager/lib/secret.ts +++ b/packages/@aws-cdk/aws-secretsmanager/lib/secret.ts @@ -161,14 +161,44 @@ export interface SecretProps { * to the CloudFormation template (via the AWS Console, SDKs, or CLI). * * Specifies text data that you want to encrypt and store in this new version of the secret. - * May be a simple string value, or a string representation of a JSON structure. + * May be a simple string value. To provide a string representation of JSON structure, use {@link SecretProps.secretObjectValue} instead. * - * Only one of `secretStringBeta1`, `secretStringValue`, and `generateSecretString` can be provided. + * Only one of `secretStringBeta1`, `secretStringValue`, 'secretObjectValue', and `generateSecretString` can be provided. * * @default - SecretsManager generates a new secret value. */ readonly secretStringValue?: SecretValue; + /** + * Initial value for a JSON secret + * + * **NOTE:** *It is **highly** encouraged to leave this field undefined and allow SecretsManager to create the secret value. + * The secret object -- if provided -- will be included in the output of the cdk as part of synthesis, + * and will appear in the CloudFormation template in the console. This can be secure(-ish) if that value is merely reference to + * another resource (or one of its attributes), but if the value is a plaintext string, it will be visible to anyone with access + * to the CloudFormation template (via the AWS Console, SDKs, or CLI). + * + * Specifies a JSON object that you want to encrypt and store in this new version of the secret. + * To specify a simple string value instead, use {@link SecretProps.secretStringValue} + * + * Only one of `secretStringBeta1`, `secretStringValue`, 'secretObjectValue', and `generateSecretString` can be provided. + * + * @example + * declare const user: iam.User; + * declare const accessKey: iam.AccessKey; + * declare const stack: Stack; + * new secretsmanager.Secret(stack, 'JSONSecret', { + * secretObjectValue: { + * username: SecretValue.unsafePlainText(user.userName), // intrinsic reference, not exposed as plaintext + * database: SecretValue.unsafePlainText('foo'), // rendered as plain text, but not a secret + * password: accessKey.secretAccessKey, // SecretValue + * }, + * }); + * + * @default - SecretsManager generates a new secret value. + */ + readonly secretObjectValue?: { [key: string]: SecretValue }; + /** * Policy to apply when the secret is removed from this stack. * @@ -233,7 +263,7 @@ export class SecretStringValueBeta1 { * // Creates a new IAM user, access and secret keys, and stores the secret access key in a Secret. * const user = new iam.User(this, 'User'); * const accessKey = new iam.AccessKey(this, 'AccessKey', { user }); - * const secret = new secrets.Secret(this, 'Secret', { + * const secret = new secretsmanager.Secret(this, 'Secret', { * secretStringValue: accessKey.secretAccessKey, * }); * ``` @@ -582,11 +612,17 @@ export class Secret extends SecretBase { throw new Error('`secretStringTemplate` and `generateStringKey` must be specified together.'); } - if ((props.generateSecretString ? 1 : 0) + (props.secretStringBeta1 ? 1 : 0) + (props.secretStringValue ? 1 : 0) > 1) { - throw new Error('Cannot specify more than one of `generateSecretString`, `secretStringValue`, and `secretStringBeta1`.'); + if ((props.generateSecretString ? 1 : 0) + + (props.secretStringBeta1 ? 1 : 0) + + (props.secretStringValue ? 1 : 0) + + (props.secretObjectValue ? 1 : 0) + > 1) { + throw new Error('Cannot specify more than one of `generateSecretString`, `secretStringValue`, `secretObjectValue`, and `secretStringBeta1`.'); } - const secretString = props.secretStringValue?.unsafeUnwrap() ?? props.secretStringBeta1?.secretValue(); + const secretString = props.secretObjectValue + ? this.resolveSecretObjectValue(props.secretObjectValue) + : props.secretStringValue?.unsafeUnwrap() ?? props.secretStringBeta1?.secretValue(); const resource = new secretsmanager.CfnSecret(this, 'Resource', { description: props.description, @@ -627,6 +663,14 @@ export class Secret extends SecretBase { this.excludeCharacters = props.generateSecretString?.excludeCharacters; } + private resolveSecretObjectValue(secretObject: { [key: string]: SecretValue }): string { + const resolvedObject: { [key: string]: string } = {}; + for (const [key, value] of Object.entries(secretObject)) { + resolvedObject[key] = value.unsafeUnwrap(); + } + return JSON.stringify(resolvedObject); + } + /** * Adds a target attachment to the secret. * @@ -968,4 +1012,4 @@ function attachmentTargetTypeToString(x: AttachmentTargetType): string { case AttachmentTargetType.DOCDB_DB_CLUSTER: return 'AWS::DocDB::DBCluster'; } -} \ No newline at end of file +} diff --git a/packages/@aws-cdk/aws-secretsmanager/rosetta/default.ts-fixture b/packages/@aws-cdk/aws-secretsmanager/rosetta/default.ts-fixture index e05db08905105..64fc649d2d1c2 100644 --- a/packages/@aws-cdk/aws-secretsmanager/rosetta/default.ts-fixture +++ b/packages/@aws-cdk/aws-secretsmanager/rosetta/default.ts-fixture @@ -1,6 +1,6 @@ // Fixture with packages imported, but nothing else import { Construct } from 'constructs'; -import { Duration, Stack } from '@aws-cdk/core'; +import { Duration, Stack, SecretValue } from '@aws-cdk/core'; import * as secretsmanager from '@aws-cdk/aws-secretsmanager'; import * as kms from '@aws-cdk/aws-kms'; import * as iam from '@aws-cdk/aws-iam'; @@ -12,4 +12,4 @@ class Fixture extends Stack { /// here } -} \ No newline at end of file +} diff --git a/packages/@aws-cdk/aws-secretsmanager/test/integ.secret.lit.ts b/packages/@aws-cdk/aws-secretsmanager/test/integ.secret.lit.ts index 968a84337c800..3c511c03ee8ca 100644 --- a/packages/@aws-cdk/aws-secretsmanager/test/integ.secret.lit.ts +++ b/packages/@aws-cdk/aws-secretsmanager/test/integ.secret.lit.ts @@ -1,5 +1,6 @@ import * as iam from '@aws-cdk/aws-iam'; import * as cdk from '@aws-cdk/core'; +import { SecretValue } from '@aws-cdk/core'; import * as secretsmanager from '../lib'; class SecretsManagerStack extends cdk.Stack { @@ -37,10 +38,20 @@ class SecretsManagerStack extends cdk.Stack { new secretsmanager.Secret(this, 'PredefinedSecret', { secretStringValue: accessKey.secretAccessKey, }); + + // JSON secret + new secretsmanager.Secret(this, 'JSONSecret', { + secretObjectValue: { + username: SecretValue.unsafePlainText(user.userName), + database: SecretValue.unsafePlainText('foo'), + password: accessKey.secretAccessKey, + }, + }); /// !hide } } const app = new cdk.App(); new SecretsManagerStack(app, 'Integ-SecretsManager-Secret'); + app.synth(); diff --git a/packages/@aws-cdk/aws-secretsmanager/test/secret.lit.integ.snapshot/Integ-SecretsManager-Secret.assets.json b/packages/@aws-cdk/aws-secretsmanager/test/secret.lit.integ.snapshot/Integ-SecretsManager-Secret.assets.json deleted file mode 100644 index af60091dfed5f..0000000000000 --- a/packages/@aws-cdk/aws-secretsmanager/test/secret.lit.integ.snapshot/Integ-SecretsManager-Secret.assets.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "version": "17.0.0", - "files": { - "693c390ede695f635dd57c39306695df3b3030b9d0a594a87198632d063d477f": { - "source": { - "path": "Integ-SecretsManager-Secret.template.json", - "packaging": "file" - }, - "destinations": { - "current_account-current_region": { - "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", - "objectKey": "693c390ede695f635dd57c39306695df3b3030b9d0a594a87198632d063d477f.json", - "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" - } - } - } - }, - "dockerImages": {} -} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-secretsmanager/test/secret.lit.integ.snapshot/Integ-SecretsManager-Secret.template.json b/packages/@aws-cdk/aws-secretsmanager/test/secret.lit.integ.snapshot/Integ-SecretsManager-Secret.template.json index fad361e1fa06c..e349aad350f9e 100644 --- a/packages/@aws-cdk/aws-secretsmanager/test/secret.lit.integ.snapshot/Integ-SecretsManager-Secret.template.json +++ b/packages/@aws-cdk/aws-secretsmanager/test/secret.lit.integ.snapshot/Integ-SecretsManager-Secret.template.json @@ -147,6 +147,32 @@ }, "UpdateReplacePolicy": "Delete", "DeletionPolicy": "Delete" + }, + "JSONSecret6FE68AEF": { + "Type": "AWS::SecretsManager::Secret", + "Properties": { + "SecretString": { + "Fn::Join": [ + "", + [ + "{\"username\":\"", + { + "Ref": "User00B015A1" + }, + "\",\"database\":\"foo\",\"password\":\"", + { + "Fn::GetAtt": [ + "AccessKeyE6B25659", + "SecretAccessKey" + ] + }, + "\"}" + ] + ] + } + }, + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" } } } \ No newline at end of file diff --git a/packages/@aws-cdk/aws-secretsmanager/test/secret.lit.integ.snapshot/SecretTestDefaultTestDeployAssert519F6A06.template.json b/packages/@aws-cdk/aws-secretsmanager/test/secret.lit.integ.snapshot/SecretTestDefaultTestDeployAssert519F6A06.template.json new file mode 100644 index 0000000000000..9e26dfeeb6e64 --- /dev/null +++ b/packages/@aws-cdk/aws-secretsmanager/test/secret.lit.integ.snapshot/SecretTestDefaultTestDeployAssert519F6A06.template.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-secretsmanager/test/secret.lit.integ.snapshot/cdk.out b/packages/@aws-cdk/aws-secretsmanager/test/secret.lit.integ.snapshot/cdk.out index 90bef2e09ad39..588d7b269d34f 100644 --- a/packages/@aws-cdk/aws-secretsmanager/test/secret.lit.integ.snapshot/cdk.out +++ b/packages/@aws-cdk/aws-secretsmanager/test/secret.lit.integ.snapshot/cdk.out @@ -1 +1 @@ -{"version":"17.0.0"} \ No newline at end of file +{"version":"20.0.0"} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-secretsmanager/test/secret.lit.integ.snapshot/integ.json b/packages/@aws-cdk/aws-secretsmanager/test/secret.lit.integ.snapshot/integ.json index 0acf5b3adbea4..acdf25c5c415c 100644 --- a/packages/@aws-cdk/aws-secretsmanager/test/secret.lit.integ.snapshot/integ.json +++ b/packages/@aws-cdk/aws-secretsmanager/test/secret.lit.integ.snapshot/integ.json @@ -1,14 +1,11 @@ { - "version": "18.0.0", + "version": "20.0.0", "testCases": { - "aws-secretsmanager/test/integ.secret.lit": { + "SecretTest/DefaultTest": { "stacks": [ "Integ-SecretsManager-Secret" ], - "diffAssets": false, - "stackUpdateWorkflow": true + "assertionStack": "SecretTestDefaultTestDeployAssert519F6A06" } - }, - "synthContext": {}, - "enableLookups": false + } } \ No newline at end of file diff --git a/packages/@aws-cdk/aws-secretsmanager/test/secret.lit.integ.snapshot/manifest.json b/packages/@aws-cdk/aws-secretsmanager/test/secret.lit.integ.snapshot/manifest.json index adce184e5bb6a..bf5a6b403c293 100644 --- a/packages/@aws-cdk/aws-secretsmanager/test/secret.lit.integ.snapshot/manifest.json +++ b/packages/@aws-cdk/aws-secretsmanager/test/secret.lit.integ.snapshot/manifest.json @@ -1,5 +1,5 @@ { - "version": "17.0.0", + "version": "20.0.0", "artifacts": { "Tree": { "type": "cdk:tree", @@ -62,9 +62,24 @@ "type": "aws:cdk:logicalId", "data": "PredefinedSecret660AF4EC" } + ], + "/Integ-SecretsManager-Secret/JSONSecret/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "JSONSecret6FE68AEF" + } ] }, "displayName": "Integ-SecretsManager-Secret" + }, + "SecretTestDefaultTestDeployAssert519F6A06": { + "type": "aws:cloudformation:stack", + "environment": "aws://unknown-account/unknown-region", + "properties": { + "templateFile": "SecretTestDefaultTestDeployAssert519F6A06.template.json", + "validateOnSynth": false + }, + "displayName": "SecretTest/DefaultTest/DeployAssert" } } } \ No newline at end of file diff --git a/packages/@aws-cdk/aws-secretsmanager/test/secret.lit.integ.snapshot/tree.json b/packages/@aws-cdk/aws-secretsmanager/test/secret.lit.integ.snapshot/tree.json index 7cd60f1c275c6..df648bb65be44 100644 --- a/packages/@aws-cdk/aws-secretsmanager/test/secret.lit.integ.snapshot/tree.json +++ b/packages/@aws-cdk/aws-secretsmanager/test/secret.lit.integ.snapshot/tree.json @@ -9,7 +9,7 @@ "path": "Tree", "constructInfo": { "fqn": "constructs.Construct", - "version": "10.0.9" + "version": "10.1.33" } }, "Integ-SecretsManager-Secret": { @@ -301,12 +301,90 @@ "fqn": "@aws-cdk/aws-secretsmanager.Secret", "version": "0.0.0" } + }, + "JSONSecret": { + "id": "JSONSecret", + "path": "Integ-SecretsManager-Secret/JSONSecret", + "children": { + "Resource": { + "id": "Resource", + "path": "Integ-SecretsManager-Secret/JSONSecret/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::SecretsManager::Secret", + "aws:cdk:cloudformation:props": { + "secretString": { + "Fn::Join": [ + "", + [ + "{\"username\":\"", + { + "Ref": "User00B015A1" + }, + "\",\"database\":\"foo\",\"password\":\"", + { + "Fn::GetAtt": [ + "AccessKeyE6B25659", + "SecretAccessKey" + ] + }, + "\"}" + ] + ] + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-secretsmanager.CfnSecret", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-secretsmanager.Secret", + "version": "0.0.0" + } } }, "constructInfo": { "fqn": "@aws-cdk/core.Stack", "version": "0.0.0" } + }, + "SecretTest": { + "id": "SecretTest", + "path": "SecretTest", + "children": { + "DefaultTest": { + "id": "DefaultTest", + "path": "SecretTest/DefaultTest", + "children": { + "Default": { + "id": "Default", + "path": "SecretTest/DefaultTest/Default", + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.1.33" + } + }, + "DeployAssert": { + "id": "DeployAssert", + "path": "SecretTest/DefaultTest/DeployAssert", + "constructInfo": { + "fqn": "@aws-cdk/core.Stack", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/integ-tests.IntegTestCase", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/integ-tests.IntegTest", + "version": "0.0.0" + } } }, "constructInfo": { diff --git a/packages/@aws-cdk/aws-secretsmanager/test/secret.test.ts b/packages/@aws-cdk/aws-secretsmanager/test/secret.test.ts index 6caa06421d04e..af98a771f7ff6 100644 --- a/packages/@aws-cdk/aws-secretsmanager/test/secret.test.ts +++ b/packages/@aws-cdk/aws-secretsmanager/test/secret.test.ts @@ -1335,3 +1335,57 @@ test('with replication regions', () => { ], }); }); + +describe('secretObjectValue', () => { + test('can be used with a mixture of plain text and SecretValue', () => { + const user = new iam.User(stack, 'User'); + const accessKey = new iam.AccessKey(stack, 'MyKey', { user }); + new secretsmanager.Secret(stack, 'Secret', { + secretObjectValue: { + username: cdk.SecretValue.unsafePlainText('username'), + password: accessKey.secretAccessKey, + }, + }); + + Template.fromStack(stack).hasResourceProperties('AWS::SecretsManager::Secret', { + GenerateSecretString: Match.absent(), + SecretString: { + 'Fn::Join': [ + '', + [ + '{"username":"username","password":"', + { 'Fn::GetAtt': ['MyKey6AB29FA6', 'SecretAccessKey'] }, + '"}', + ], + ], + }, + }); + }); + + test('can be used with a mixture of plain text and SecretValue, with feature flag', () => { + const featureStack = new cdk.Stack(); + featureStack.node.setContext('@aws-cdk/core:checkSecretUsage', true); + const user = new iam.User(featureStack, 'User'); + const accessKey = new iam.AccessKey(featureStack, 'MyKey', { user }); + new secretsmanager.Secret(featureStack, 'Secret', { + secretObjectValue: { + username: cdk.SecretValue.unsafePlainText('username'), + password: accessKey.secretAccessKey, + }, + }); + + Template.fromStack(featureStack).hasResourceProperties('AWS::SecretsManager::Secret', { + GenerateSecretString: Match.absent(), + SecretString: { + 'Fn::Join': [ + '', + [ + '{"username":"username","password":"', + { 'Fn::GetAtt': ['MyKey6AB29FA6', 'SecretAccessKey'] }, + '"}', + ], + ], + }, + }); + }); +}); diff --git a/packages/@aws-cdk/core/lib/secret-value.ts b/packages/@aws-cdk/core/lib/secret-value.ts index ebbf471896cba..12e747f2592eb 100644 --- a/packages/@aws-cdk/core/lib/secret-value.ts +++ b/packages/@aws-cdk/core/lib/secret-value.ts @@ -68,7 +68,18 @@ export class SecretValue extends Intrinsic { * will be visible to anyone who has access to the CloudFormation template * (via the AWS Console, SDKs, or CLI). * - * The only reasonable use case for using this method is when you are testing. + * The primary use case for using this method is when you are testing. + * + * The other use case where this is appropriate is when constructing a JSON secret. + * For example, a JSON secret might have multiple fields where only some are actual + * secret values. + * + * @example + * declare const secret: SecretValue; + * const jsonSecret = { + * username: SecretValue.unsafePlainText('myUsername'), + * password: secret, + * }; */ public static unsafePlainText(secret: string): SecretValue { return new SecretValue(secret); @@ -254,4 +265,4 @@ Object.defineProperty(SecretValue.prototype, SECRET_VALUE_SYM, { configurable: false, enumerable: false, writable: false, -}); \ No newline at end of file +}); From 69d25a6f26f03c6589b350803431de23fe598ae0 Mon Sep 17 00:00:00 2001 From: Constantin Ponfick <95411546+cponfick-bhs@users.noreply.github.com> Date: Wed, 13 Jul 2022 20:42:33 +0200 Subject: [PATCH 31/92] feat(appsync): set max batch size when using batch invoke (#20995) closes #20467 ---- ### All Submissions: * [x] Have you followed the guidelines in our [Contributing guide?](https://github.com/aws/aws-cdk/blob/main/CONTRIBUTING.md) ### Adding new Unconventional Dependencies: * [ ] This PR adds new unconventional dependencies following the process described [here](https://github.com/aws/aws-cdk/blob/main/CONTRIBUTING.md/#adding-new-unconventional-dependencies) ### New Features * [x] Have you added the new feature to an [integration test](https://github.com/aws/aws-cdk/blob/main/INTEGRATION_TESTS.md)? * [x] Did you use `yarn integ` to deploy the infrastructure and generate the snapshot (i.e. `yarn integ` without `--dry-run`)? *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- packages/@aws-cdk/aws-appsync/lib/resolver.ts | 7 +++++++ .../aws-appsync/test/appsync-mapping-template.test.ts | 2 ++ packages/@aws-cdk/aws-appsync/test/appsync.lambda.graphql | 1 + .../@aws-cdk/aws-appsync/test/integ.appsync-lambda.ts | 8 ++++++++ .../aws-appsync/test/verify.integ.appsync-lambda.sh | 2 +- .../test/verify/lambda-tutorial/lambda-tutorial.js | 1 + 6 files changed, 20 insertions(+), 1 deletion(-) diff --git a/packages/@aws-cdk/aws-appsync/lib/resolver.ts b/packages/@aws-cdk/aws-appsync/lib/resolver.ts index e2e2a48d8a5db..3df7f44a068c5 100644 --- a/packages/@aws-cdk/aws-appsync/lib/resolver.ts +++ b/packages/@aws-cdk/aws-appsync/lib/resolver.ts @@ -45,6 +45,12 @@ export interface BaseResolverProps { * @default - No caching configuration */ readonly cachingConfig?: CachingConfig; + /** + * The maximum number of elements per batch, when using batch invoke + * + * @default - No max batch size + */ + readonly maxBatchSize?: number; } /** @@ -112,6 +118,7 @@ export class Resolver extends Construct { requestMappingTemplate: props.requestMappingTemplate ? props.requestMappingTemplate.renderTemplate() : undefined, responseMappingTemplate: props.responseMappingTemplate ? props.responseMappingTemplate.renderTemplate() : undefined, cachingConfig: this.createCachingConfig(props.cachingConfig), + maxBatchSize: props.maxBatchSize, }); props.api.addSchemaDependency(this.resolver); if (props.dataSource) { diff --git a/packages/@aws-cdk/aws-appsync/test/appsync-mapping-template.test.ts b/packages/@aws-cdk/aws-appsync/test/appsync-mapping-template.test.ts index 5617c16255b71..b01403a7ba9fc 100644 --- a/packages/@aws-cdk/aws-appsync/test/appsync-mapping-template.test.ts +++ b/packages/@aws-cdk/aws-appsync/test/appsync-mapping-template.test.ts @@ -57,12 +57,14 @@ describe('Lambda Mapping Templates', () => { fieldName: 'relatedPosts', requestMappingTemplate: appsync.MappingTemplate.lambdaRequest('$util.toJson($ctx)', 'BatchInvoke'), responseMappingTemplate: appsync.MappingTemplate.lambdaResult(), + maxBatchSize: 10, }); // THEN Template.fromStack(stack).hasResourceProperties('AWS::AppSync::Resolver', { FieldName: 'relatedPosts', RequestMappingTemplate: batchMT, + MaxBatchSize: 10, }); }); }); \ No newline at end of file diff --git a/packages/@aws-cdk/aws-appsync/test/appsync.lambda.graphql b/packages/@aws-cdk/aws-appsync/test/appsync.lambda.graphql index 0d7af74efd581..089553dfc913b 100644 --- a/packages/@aws-cdk/aws-appsync/test/appsync.lambda.graphql +++ b/packages/@aws-cdk/aws-appsync/test/appsync.lambda.graphql @@ -21,4 +21,5 @@ type Post { ups: Int downs: Int relatedPosts: [Post] + relatedPostsMaxBatchSize: [Post] } \ No newline at end of file diff --git a/packages/@aws-cdk/aws-appsync/test/integ.appsync-lambda.ts b/packages/@aws-cdk/aws-appsync/test/integ.appsync-lambda.ts index 100ddba5e0871..b60dd5e23223d 100644 --- a/packages/@aws-cdk/aws-appsync/test/integ.appsync-lambda.ts +++ b/packages/@aws-cdk/aws-appsync/test/integ.appsync-lambda.ts @@ -67,6 +67,7 @@ lambdaDS.createResolver({ requestMappingTemplate: appsync.MappingTemplate.lambdaRequest(requestPayload('addPost', { withArgs: true })), responseMappingTemplate, }); + lambdaDS.createResolver({ typeName: 'Post', fieldName: 'relatedPosts', @@ -74,5 +75,12 @@ lambdaDS.createResolver({ responseMappingTemplate, }); +lambdaDS.createResolver({ + typeName: 'Post', + fieldName: 'relatedPostsMaxBatchSize', + requestMappingTemplate: appsync.MappingTemplate.lambdaRequest(requestPayload('relatedPostsMaxBatchSize', { withSource: true }), 'BatchInvoke'), + responseMappingTemplate, + maxBatchSize: 2, +}); app.synth(); \ No newline at end of file diff --git a/packages/@aws-cdk/aws-appsync/test/verify.integ.appsync-lambda.sh b/packages/@aws-cdk/aws-appsync/test/verify.integ.appsync-lambda.sh index b21e88e8b1ef6..fa116371fcb34 100644 --- a/packages/@aws-cdk/aws-appsync/test/verify.integ.appsync-lambda.sh +++ b/packages/@aws-cdk/aws-appsync/test/verify.integ.appsync-lambda.sh @@ -21,7 +21,7 @@ elif [[ "$1" == "--check" ]]; then exit 1 fi echo THIS TEST SHOULD PRODUCE A LIST OF BOOKS - curl -XPOST -H "Content-Type:application/graphql" -H "x-api-key:$2" -d '{ "query": "query { allPosts { id author title relatedPosts { id title } } }" }" }' $3 | json_pp + curl -XPOST -H "Content-Type:application/graphql" -H "x-api-key:$2" -d '{ "query": "query { allPosts { id author title relatedPosts { id title } relatedPostsMaxBatchSize { id title } } }" }" }' $3 | json_pp echo "" elif [[ "$1" == "--clean" ]];then cdk destroy --app "node integ.appsync-lambda.js" diff --git a/packages/@aws-cdk/aws-appsync/test/verify/lambda-tutorial/lambda-tutorial.js b/packages/@aws-cdk/aws-appsync/test/verify/lambda-tutorial/lambda-tutorial.js index 15f072857fc7e..5a08f83ef4f0a 100644 --- a/packages/@aws-cdk/aws-appsync/test/verify/lambda-tutorial/lambda-tutorial.js +++ b/packages/@aws-cdk/aws-appsync/test/verify/lambda-tutorial/lambda-tutorial.js @@ -19,6 +19,7 @@ exports.handler = (event, context, callback) => { console.log("Got an BatchInvoke Request. The payload has %d items to resolve.", event.length); const field = event[0].field; switch(field) { + case "relatedPostsMaxBatchSize": case "relatedPosts": var results = []; // the response MUST contain the same number From 5cff2d9d28c4be0bb72b0febd3f30311252f57f8 Mon Sep 17 00:00:00 2001 From: Steve Hodgkiss <62331+stevehodgkiss@users.noreply.github.com> Date: Wed, 13 Jul 2022 23:16:44 +0400 Subject: [PATCH 32/92] fix(core): use node.path in skip bundling check for consistency with cdk deploy CLI (#19950) The pattern given to aws-cdk on the CLI (`cdk deploy $pattern`) is matched against `stack.hierarchicalId` (i.e. displayName, node.path) [1] [2] [3]. The same pattern is given to the CDK app (as `bundlingStacks`) but is matching against `stackName`, causing potential for bundling to be skipped under certain scenarios (i.e. using --exclusively with custom stack names). To make them consistent, the skip bundling check has been changed to match against the same value as `cdk deploy $pattern` (node.path/hierarchicalId/displayName) when deciding whether to bundle an asset. Making them consistent ensures necessary stacks are bundled, avoiding the issue of uploading the entire project directory since the asset wasn't bundled. fixes #19927 [1] https://github.com/aws/aws-cdk/blob/1d0270446b3effa6b8518de3c7d76f0c14e626c5/packages/aws-cdk/lib/api/cxapp/cloud-assembly.ts#L138 [2] https://github.com/aws/aws-cdk/blob/6cca62db88866479f2b1d4f52b30beab3d78aeb2/packages/@aws-cdk/cx-api/lib/cloud-artifact.ts#L143-L145 [3] https://github.com/aws/aws-cdk/blob/6cca62db88866479f2b1d4f52b30beab3d78aeb2/packages/@aws-cdk/core/lib/stack-synthesizers/_shared.ts#L66 ---- ### All Submissions: * [ ] Have you followed the guidelines in our [Contributing guide?](https://github.com/aws/aws-cdk/blob/master/CONTRIBUTING.md) ### Adding new Unconventional Dependencies: * [ ] This PR adds new unconventional dependencies following the process described [here](https://github.com/aws/aws-cdk/blob/master/CONTRIBUTING.md/#adding-new-unconventional-dependencies) ### New Features * [ ] Have you added the new feature to an [integration test](https://github.com/aws/aws-cdk/blob/master/INTEGRATION_TESTS.md)? * [ ] Did you use `yarn integ` to deploy the infrastructure and generate the snapshot (i.e. `yarn integ` without `--dry-run`)? *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- packages/@aws-cdk/core/lib/stack.ts | 6 ++-- packages/@aws-cdk/core/test/staging.test.ts | 40 ++++++++++++++++++++- 2 files changed, 42 insertions(+), 4 deletions(-) diff --git a/packages/@aws-cdk/core/lib/stack.ts b/packages/@aws-cdk/core/lib/stack.ts index 23097a90914f2..325863d2a92fd 100644 --- a/packages/@aws-cdk/core/lib/stack.ts +++ b/packages/@aws-cdk/core/lib/stack.ts @@ -1168,10 +1168,10 @@ export class Stack extends Construct implements ITaggable { public get bundlingRequired() { const bundlingStacks: string[] = this.node.tryGetContext(cxapi.BUNDLING_STACKS) ?? ['*']; - // bundlingStacks is of the form `Stage/Stack`, convert it to `Stage-Stack` before comparing to stack name + // bundlingStacks is of the form `Stage/Stack` return bundlingStacks.some(pattern => minimatch( - this.stackName, - pattern.replace('/', '-'), + this.node.path, // the same value used for pattern matching in aws-cdk CLI (displayName / hierarchicalId) + pattern, )); } } diff --git a/packages/@aws-cdk/core/test/staging.test.ts b/packages/@aws-cdk/core/test/staging.test.ts index f577ddf3b9024..a1cf81aec2eed 100644 --- a/packages/@aws-cdk/core/test/staging.test.ts +++ b/packages/@aws-cdk/core/test/staging.test.ts @@ -844,7 +844,45 @@ describe('staging', () => { const dockerStubInput = readDockerStubInputConcat(); // Docker ran for the asset in Stack1 expect(dockerStubInput).toMatch(DockerStubCommand.SUCCESS); - // DOcker did not run for the asset in Stack2 + // Docker did not run for the asset in Stack2 + expect(dockerStubInput).not.toMatch(DockerStubCommand.MULTIPLE_FILES); + }); + + test('correctly skips bundling with stack under stage and custom stack name', () => { + // GIVEN + const app = new App(); + + const stage = new Stage(app, 'Stage'); + stage.node.setContext(cxapi.BUNDLING_STACKS, ['Stage/Stack1']); + + const stack1 = new Stack(stage, 'Stack1', { stackName: 'unrelated-stack1-name' }); + const stack2 = new Stack(stage, 'Stack2', { stackName: 'unrelated-stack2-name' }); + const directory = path.join(__dirname, 'fs', 'fixtures', 'test1'); + + // WHEN + new AssetStaging(stack1, 'Asset', { + sourcePath: directory, + assetHashType: AssetHashType.OUTPUT, + bundling: { + image: DockerImage.fromRegistry('alpine'), + command: [DockerStubCommand.SUCCESS], + }, + }); + + new AssetStaging(stack2, 'Asset', { + sourcePath: directory, + assetHashType: AssetHashType.OUTPUT, + bundling: { + image: DockerImage.fromRegistry('alpine'), + command: [DockerStubCommand.MULTIPLE_FILES], + }, + }); + + // THEN + const dockerStubInput = readDockerStubInputConcat(); + // Docker ran for the asset in Stack1 + expect(dockerStubInput).toMatch(DockerStubCommand.SUCCESS); + // Docker did not run for the asset in Stack2 expect(dockerStubInput).not.toMatch(DockerStubCommand.MULTIPLE_FILES); }); From f00f95213cbe31d0e78a01b3ada8a68eeda55efa Mon Sep 17 00:00:00 2001 From: Momo Kornher Date: Wed, 13 Jul 2022 14:13:00 +0100 Subject: [PATCH 33/92] fix(custom-resources): Custom resource provider framework not passing `ResponseURL` to user function (#21117) aws#20889 included a change that broke the custom resource framework by not including the necessary presigned URL. We attempted to fix this with aws#21065 but it didn't resolve the issue and aws#21109 reverted the attempted fix. This changes explicitly includes the presigned URL, as well as adding tests to ensure the URL is passed to the downstream Lambda Function. Closes aws#21058 ---- * [x] Have you followed the guidelines in our [Contributing guide?](https://github.com/aws/aws-cdk/blob/main/CONTRIBUTING.md) * [ ] This PR adds new unconventional dependencies following the process described [here](https://github.com/aws/aws-cdk/blob/main/CONTRIBUTING.md/#adding-new-unconventional-dependencies) * [ ] Have you added the new feature to an [integration test](https://github.com/aws/aws-cdk/blob/main/INTEGRATION_TESTS.md)? * [ ] Did you use `yarn integ` to deploy the infrastructure and generate the snapshot (i.e. `yarn integ` without `--dry-run`)? *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- .../provider-framework/runtime/framework.ts | 10 +++-- .../test/provider-framework/runtime.test.ts | 38 +++++++++++++++++++ 2 files changed, 44 insertions(+), 4 deletions(-) diff --git a/packages/@aws-cdk/custom-resources/lib/provider-framework/runtime/framework.ts b/packages/@aws-cdk/custom-resources/lib/provider-framework/runtime/framework.ts index f9586e032ae5f..20c0144311b94 100644 --- a/packages/@aws-cdk/custom-resources/lib/provider-framework/runtime/framework.ts +++ b/packages/@aws-cdk/custom-resources/lib/provider-framework/runtime/framework.ts @@ -29,7 +29,7 @@ async function onEvent(cfnRequest: AWSLambda.CloudFormationCustomResourceEvent) cfnRequest.ResourceProperties = cfnRequest.ResourceProperties || { }; - const onEventResult = await invokeUserFunction(consts.USER_ON_EVENT_FUNCTION_ARN_ENV, sanitizedRequest) as OnEventResponse; + const onEventResult = await invokeUserFunction(consts.USER_ON_EVENT_FUNCTION_ARN_ENV, sanitizedRequest, cfnRequest.ResponseURL) as OnEventResponse; log('onEvent returned:', onEventResult); // merge the request and the result from onEvent to form the complete resource event @@ -61,7 +61,7 @@ async function isComplete(event: AWSCDKAsyncCustomResource.IsCompleteRequest) { const sanitizedRequest = { ...event, ResponseURL: '...' } as const; log('isComplete', sanitizedRequest); - const isCompleteResult = await invokeUserFunction(consts.USER_IS_COMPLETE_FUNCTION_ARN_ENV, sanitizedRequest) as IsCompleteResponse; + const isCompleteResult = await invokeUserFunction(consts.USER_IS_COMPLETE_FUNCTION_ARN_ENV, sanitizedRequest, event.ResponseURL) as IsCompleteResponse; log('user isComplete returned:', isCompleteResult); // if we are not complete, return false, and don't send a response back. @@ -96,7 +96,7 @@ async function onTimeout(timeoutEvent: any) { }); } -async function invokeUserFunction(functionArnEnv: string, sanitizedPayload: A) { +async function invokeUserFunction(functionArnEnv: string, sanitizedPayload: A, responseUrl: string) { const functionArn = getEnv(functionArnEnv); log(`executing user function ${functionArn} with payload`, sanitizedPayload); @@ -105,7 +105,9 @@ async function invokeUserFunction(functionArnE // automatically by the JavaScript SDK. const resp = await invokeFunction({ FunctionName: functionArn, - Payload: JSON.stringify(sanitizedPayload), + + // Cannot strip 'ResponseURL' here as this would be a breaking change even though the downstream CR doesn't need it + Payload: JSON.stringify({ ...sanitizedPayload, ResponseURL: responseUrl }), }); log('user function response:', resp, typeof(resp)); diff --git a/packages/@aws-cdk/custom-resources/test/provider-framework/runtime.test.ts b/packages/@aws-cdk/custom-resources/test/provider-framework/runtime.test.ts index 751e33e6408e0..2d35a9e63eac9 100644 --- a/packages/@aws-cdk/custom-resources/test/provider-framework/runtime.test.ts +++ b/packages/@aws-cdk/custom-resources/test/provider-framework/runtime.test.ts @@ -18,7 +18,10 @@ outbound.httpRequest = mocks.httpRequestMock; outbound.invokeFunction = mocks.invokeFunctionMock; outbound.startExecution = mocks.startExecutionMock; +const invokeFunctionSpy = jest.spyOn(outbound, 'invokeFunction'); + beforeEach(() => mocks.setup()); +afterEach(() => invokeFunctionSpy.mockClear()); test('async flow: isComplete returns true only after 3 times', async () => { let isCompleteCalls = 0; @@ -346,6 +349,41 @@ describe('if CREATE fails, the subsequent DELETE will be ignored', () => { }); +describe('ResponseURL is passed to user function', () => { + test('for onEvent', async () => { + // GIVEN + mocks.onEventImplMock = async () => ({ PhysicalResourceId: MOCK_PHYSICAL_ID }); + + // WHEN + await simulateEvent({ + RequestType: 'Create', + }); + + // THEN + expect(invokeFunctionSpy).toHaveBeenCalledTimes(1); + expect(invokeFunctionSpy).toBeCalledWith(expect.objectContaining({ + Payload: expect.stringContaining(`"ResponseURL":"${mocks.MOCK_REQUEST.ResponseURL}"`), + })); + }); + + test('for isComplete', async () => { + // GIVEN + mocks.onEventImplMock = async () => ({ PhysicalResourceId: MOCK_PHYSICAL_ID }); + mocks.isCompleteImplMock = async () => ({ IsComplete: true }); + + // WHEN + await simulateEvent({ + RequestType: 'Create', + }); + + // THEN + expect(invokeFunctionSpy).toHaveBeenCalledTimes(2); + expect(invokeFunctionSpy).toHaveBeenLastCalledWith(expect.objectContaining({ + Payload: expect.stringContaining(`"ResponseURL":"${mocks.MOCK_REQUEST.ResponseURL}"`), + })); + }); +}); + // ----------------------------------------------------------------------------------------------------------------------- /** From 3e296449f30391a01804e95fba4f17ff2c9d9415 Mon Sep 17 00:00:00 2001 From: Kaizen Conroy Date: Wed, 13 Jul 2022 16:15:14 -0400 Subject: [PATCH 34/92] chore(release): 2.31.2 --- CHANGELOG.v2.alpha.md | 2 ++ CHANGELOG.v2.md | 7 +++++++ version.v2.json | 4 ++-- 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.v2.alpha.md b/CHANGELOG.v2.alpha.md index ab9b8fa3f9417..0990e8010d157 100644 --- a/CHANGELOG.v2.alpha.md +++ b/CHANGELOG.v2.alpha.md @@ -2,6 +2,8 @@ All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. +### [2.31.2-alpha.0](https://github.com/aws/aws-cdk/compare/v2.31.1-alpha.0...v2.31.2-alpha.0) (2022-07-13) + ## [2.31.1-alpha.0](https://github.com/aws/aws-cdk/compare/v2.31.0-alpha.0...v2.31.1-alpha.0) (2022-07-08) ## [2.31.0-alpha.0](https://github.com/aws/aws-cdk/compare/v2.30.0-alpha.0...v2.31.0-alpha.0) (2022-07-06) diff --git a/CHANGELOG.v2.md b/CHANGELOG.v2.md index cfe56b77ac27f..d903afd6ffc33 100644 --- a/CHANGELOG.v2.md +++ b/CHANGELOG.v2.md @@ -2,6 +2,13 @@ All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. +### [2.31.2](https://github.com/aws/aws-cdk/compare/v2.31.1...v2.31.2) (2022-07-13) + + +### Bug Fixes + +* **custom-resources:** Custom resource provider framework not passing `ResponseURL` to user function ([#21117](https://github.com/aws/aws-cdk/issues/21117)) ([f00f952](https://github.com/aws/aws-cdk/commit/f00f95213cbe31d0e78a01b3ada8a68eeda55efa)), closes [aws#21065](https://github.com/aws/aws/issues/21065) [aws#21109](https://github.com/aws/aws/issues/21109) [aws#21058](https://github.com/aws/aws/issues/21058) + ## [2.31.1](https://github.com/aws/aws-cdk/compare/v2.31.0...v2.31.1) (2022-07-08) diff --git a/version.v2.json b/version.v2.json index da020dcf6c61f..6a0892b49493b 100644 --- a/version.v2.json +++ b/version.v2.json @@ -1,4 +1,4 @@ { - "version": "2.31.1", - "alphaVersion": "2.31.1-alpha.0" + "version": "2.31.2", + "alphaVersion": "2.31.2-alpha.0" } \ No newline at end of file From 8b2e4a3e5ec171b5dc94656108d7d99969c3f597 Mon Sep 17 00:00:00 2001 From: Kaizen Conroy Date: Wed, 13 Jul 2022 16:17:25 -0400 Subject: [PATCH 35/92] update changelog --- CHANGELOG.v2.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.v2.md b/CHANGELOG.v2.md index d903afd6ffc33..fef997ae1539c 100644 --- a/CHANGELOG.v2.md +++ b/CHANGELOG.v2.md @@ -2,7 +2,7 @@ All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. -### [2.31.2](https://github.com/aws/aws-cdk/compare/v2.31.1...v2.31.2) (2022-07-13) +## [2.31.2](https://github.com/aws/aws-cdk/compare/v2.31.1...v2.31.2) (2022-07-13) ### Bug Fixes From 0ccf919afbd3cc668783c973153e64f8aaf36319 Mon Sep 17 00:00:00 2001 From: Kaizen Conroy Date: Wed, 13 Jul 2022 16:54:51 -0400 Subject: [PATCH 36/92] update alpha changelog --- CHANGELOG.v2.alpha.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.v2.alpha.md b/CHANGELOG.v2.alpha.md index 0990e8010d157..a63e797cb9131 100644 --- a/CHANGELOG.v2.alpha.md +++ b/CHANGELOG.v2.alpha.md @@ -2,7 +2,7 @@ All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. -### [2.31.2-alpha.0](https://github.com/aws/aws-cdk/compare/v2.31.1-alpha.0...v2.31.2-alpha.0) (2022-07-13) +## [2.31.2-alpha.0](https://github.com/aws/aws-cdk/compare/v2.31.1-alpha.0...v2.31.2-alpha.0) (2022-07-13) ## [2.31.1-alpha.0](https://github.com/aws/aws-cdk/compare/v2.31.0-alpha.0...v2.31.1-alpha.0) (2022-07-08) From 8dc083b8f0522a2f7fc3e9ff974f4aa1f9bbb96e Mon Sep 17 00:00:00 2001 From: josephedward <15126922+josephedward@users.noreply.github.com> Date: Wed, 13 Jul 2022 18:08:04 -0400 Subject: [PATCH 37/92] docs(aws-ssm): StringParameter example is wrong (#21127) adding indentation as requested by reviewer fixes #20763 ---- ### All Submissions: * [ ] Have you followed the guidelines in our [Contributing guide?](https://github.com/aws/aws-cdk/blob/main/CONTRIBUTING.md) ### Adding new Unconventional Dependencies: * [ ] This PR adds new unconventional dependencies following the process described [here](https://github.com/aws/aws-cdk/blob/main/CONTRIBUTING.md/#adding-new-unconventional-dependencies) ### New Features * [ ] Have you added the new feature to an [integration test](https://github.com/aws/aws-cdk/blob/main/INTEGRATION_TESTS.md)? * [ ] Did you use `yarn integ` to deploy the infrastructure and generate the snapshot (i.e. `yarn integ` without `--dry-run`)? *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- packages/@aws-cdk/aws-ssm/lib/parameter.ts | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/packages/@aws-cdk/aws-ssm/lib/parameter.ts b/packages/@aws-cdk/aws-ssm/lib/parameter.ts index cc66f65098300..9d4edf34401b8 100644 --- a/packages/@aws-cdk/aws-ssm/lib/parameter.ts +++ b/packages/@aws-cdk/aws-ssm/lib/parameter.ts @@ -329,6 +329,27 @@ export interface SecureStringParameterAttributes extends CommonStringParameterAt /** * Creates a new String SSM Parameter. * @resource AWS::SSM::Parameter + * + * @example + * + * const ssmParameter = new ssm.StringParameter(this, 'mySsmParameter', { + * parameterName: 'mySsmParameter', + * stringValue: 'mySsmParameterValue', + * type: ssm.ParameterType.STRING, + * }); + * + * const secureParameter = new ssm.StringParameter(this, 'mySecretParameter', { + * parameterName: 'mySecretParameter', + * stringValue: 'mySecretParameterValue', + * type: ssm.ParameterType.SECURE_STRING, + * }); + * + * const listParameter = new ssm.StringParameter(this, 'myListParameter', { + * parameterName: 'myListParameter', + * stringValue: ["myListParameterValue1", "myListParameterValue2"], + * type: ssm.ParameterType.STRING_LIST, + * }); + * */ export class StringParameter extends ParameterBase implements IStringParameter { From f15cb27a3ebae163cb4ce5320d9f2603dd9c6b51 Mon Sep 17 00:00:00 2001 From: Mitchell Valine Date: Wed, 13 Jul 2022 16:21:12 -0700 Subject: [PATCH 38/92] chore: remove note of go being in dev preview (#21130) Removes note in go app init template readmes saying go is in dev preview since it is not generally available. Also adds note about `cdk.json` which may be useful for new users and is present in other language templates. references: https://github.com/awsdocs/aws-cdk-guide/issues/416 ---- ### All Submissions: * [X] Have you followed the guidelines in our [Contributing guide?](https://github.com/aws/aws-cdk/blob/main/CONTRIBUTING.md) ### Adding new Unconventional Dependencies: * [ ] This PR adds new unconventional dependencies following the process described [here](https://github.com/aws/aws-cdk/blob/main/CONTRIBUTING.md/#adding-new-unconventional-dependencies) ### New Features * [ ] Have you added the new feature to an [integration test](https://github.com/aws/aws-cdk/blob/main/INTEGRATION_TESTS.md)? * [ ] Did you use `yarn integ` to deploy the infrastructure and generate the snapshot (i.e. `yarn integ` without `--dry-run`)? *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- packages/aws-cdk/lib/init-templates/app/go/README.md | 4 +--- packages/aws-cdk/lib/init-templates/sample-app/go/README.md | 4 +--- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/packages/aws-cdk/lib/init-templates/app/go/README.md b/packages/aws-cdk/lib/init-templates/app/go/README.md index 5487d820bcc15..79e5c4555ce24 100644 --- a/packages/aws-cdk/lib/init-templates/app/go/README.md +++ b/packages/aws-cdk/lib/init-templates/app/go/README.md @@ -2,9 +2,7 @@ This is a blank project for CDK development with Go. -**NOTICE**: Go support is still in Developer Preview. This implies that APIs may -change while we address early feedback from the community. We would love to hear -about your experience through GitHub issues. +The `cdk.json` file tells the CDK toolkit how to execute your app. ## Useful commands diff --git a/packages/aws-cdk/lib/init-templates/sample-app/go/README.md b/packages/aws-cdk/lib/init-templates/sample-app/go/README.md index 9b8d4b29f26e3..7667f2ed2f7e7 100644 --- a/packages/aws-cdk/lib/init-templates/sample-app/go/README.md +++ b/packages/aws-cdk/lib/init-templates/sample-app/go/README.md @@ -2,9 +2,7 @@ This is a blank project for Go development with CDK. -**NOTICE**: Go support is still in Developer Preview. This implies that APIs may -change while we address early feedback from the community. We would love to hear -about your experience through GitHub issues. +The `cdk.json` file tells the CDK Toolkit how to execute your app. ## Useful commands From 39a7c1f0bafb1cf3f51fbe09053e443c0d87487e Mon Sep 17 00:00:00 2001 From: Andrew Gillis Date: Thu, 14 Jul 2022 01:39:02 +0100 Subject: [PATCH 39/92] feat(cli): --force flag and glob-style key matches for context --reset (#19890) New pull request for glob style key matches in context --reset (#19840) Adds the `--force` flag to ignore missing key errors. This is in response to #19888 ---- ### All Submissions: * [x] Have you followed the guidelines in our [Contributing guide?](https://github.com/aws/aws-cdk/blob/master/CONTRIBUTING.md) ### Adding new Unconventional Dependencies: * [ ] This PR adds new unconventional dependencies following the process described [here](https://github.com/aws/aws-cdk/blob/master/CONTRIBUTING.md/#adding-new-unconventional-dependencies) ### New Features * [ ] Have you added the new feature to an [integration test](https://github.com/aws/aws-cdk/blob/master/INTEGRATION_TESTS.md)? * [ ] Did you use `yarn integ` to deploy the infrastructure and generate the snapshot (i.e. `yarn integ` without `--dry-run`)? *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- packages/aws-cdk/lib/cli.ts | 1 + packages/aws-cdk/lib/commands/context.ts | 87 +++++- .../test/commands/context-command.test.ts | 265 +++++++++++++++--- 3 files changed, 295 insertions(+), 58 deletions(-) diff --git a/packages/aws-cdk/lib/cli.ts b/packages/aws-cdk/lib/cli.ts index c037a24ba9d33..7c2f5227ff640 100644 --- a/packages/aws-cdk/lib/cli.ts +++ b/packages/aws-cdk/lib/cli.ts @@ -238,6 +238,7 @@ async function parseCommandLineArguments() { ) .command('context', 'Manage cached context values', (yargs: Argv) => yargs .option('reset', { alias: 'e', desc: 'The context key (or its index) to reset', type: 'string', requiresArg: true }) + .option('force', { alias: 'f', desc: 'Ignore missing key error', type: 'boolean', default: false }) .option('clear', { desc: 'Clear all context', type: 'boolean' })) .command(['docs', 'doc'], 'Opens the reference documentation in a browser', (yargs: Argv) => yargs .option('browser', { diff --git a/packages/aws-cdk/lib/commands/context.ts b/packages/aws-cdk/lib/commands/context.ts index 21ec2f923e37d..52600e9a4f5db 100644 --- a/packages/aws-cdk/lib/commands/context.ts +++ b/packages/aws-cdk/lib/commands/context.ts @@ -1,19 +1,19 @@ import * as chalk from 'chalk'; +import * as minimatch from 'minimatch'; import * as version from '../../lib/version'; import { CommandOptions } from '../command-api'; -import { print } from '../logging'; -import { Context, PROJECT_CONFIG } from '../settings'; +import { print, error, warning } from '../logging'; +import { Context, PROJECT_CONFIG, PROJECT_CONTEXT, USER_DEFAULTS } from '../settings'; import { renderTable } from '../util'; export async function realHandler(options: CommandOptions): Promise { const { configuration, args } = options; - if (args.clear) { configuration.context.clear(); await configuration.saveContext(); print('All context values cleared.'); } else if (args.reset) { - invalidateContext(configuration.context, args.reset as string); + invalidateContext(configuration.context, args.reset as string, args.force as boolean); await configuration.saveContext(); } else { // List -- support '--json' flag @@ -48,30 +48,93 @@ function listContext(context: Context) { const jsonWithoutNewlines = JSON.stringify(context.all[key], undefined, 2).replace(/\s+/g, ' '); data.push([i, key, jsonWithoutNewlines]); } - - print(`Context found in ${chalk.blue(PROJECT_CONFIG)}:\n`); - + print('Context found in %s:', chalk.blue(PROJECT_CONFIG)); + print(''); print(renderTable(data, process.stdout.columns)); // eslint-disable-next-line max-len print(`Run ${chalk.blue('cdk context --reset KEY_OR_NUMBER')} to remove a context key. It will be refreshed on the next CDK synthesis run.`); } -function invalidateContext(context: Context, key: string) { +function invalidateContext(context: Context, key: string, force: boolean) { const i = parseInt(key, 10); if (`${i}` === key) { // was a number and we fully parsed it. key = keyByNumber(context, i); } - // Unset! if (context.has(key)) { context.unset(key); - print(`Context value ${chalk.blue(key)} reset. It will be refreshed on next synthesis`); - } else { - print(`No context value with key ${chalk.blue(key)}`); + // check if the value was actually unset. + if (!context.has(key)) { + print('Context value %s reset. It will be refreshed on next synthesis', chalk.blue(key)); + return; + } + + // Value must be in readonly bag + error('Only context values specified in %s can be reset through the CLI', chalk.blue(PROJECT_CONTEXT)); + if (!force) { + throw new Error(`Cannot reset readonly context value with key: ${key}`); + } + } + + // check if value is expression matching keys + const matches = keysByExpression(context, key); + + if (matches.length > 0) { + + matches.forEach((match) => { + context.unset(match); + }); + + const { unset, readonly } = getUnsetAndReadonly(context, matches); + + // output the reset values + printUnset(unset); + + // warn about values not reset + printReadonly(readonly); + + // throw when none of the matches were reset + if (!force && unset.length === 0) { + throw new Error('None of the matched context values could be reset'); + } + return; + } + if (!force) { + throw new Error(`No context value matching key: ${key}`); } } +function printUnset(unset: string[]) { + if (unset.length === 0) return; + print('The following matched context values reset. They will be refreshed on next synthesis'); + unset.forEach((match) => { + print(' %s', match); + }); +} +function printReadonly(readonly: string[]) { + if (readonly.length === 0) return; + warning('The following matched context values could not be reset through the CLI'); + readonly.forEach((match) => { + print(' %s', match); + }); + print(''); + print('This usually means they are configured in %s or %s', chalk.blue(PROJECT_CONFIG), chalk.blue(USER_DEFAULTS)); +} +function keysByExpression(context: Context, expression: string) { + return context.keys.filter(minimatch.filter(expression)); +} + +function getUnsetAndReadonly(context: Context, matches: string[]) { + return matches.reduce<{ unset: string[], readonly: string[] }>((acc, match) => { + if (context.has(match)) { + acc.readonly.push(match); + } else { + acc.unset.push(match); + } + return acc; + }, { unset: [], readonly: [] }); +} function keyByNumber(context: Context, n: number) { for (const [i, key] of contextKeys(context)) { diff --git a/packages/aws-cdk/test/commands/context-command.test.ts b/packages/aws-cdk/test/commands/context-command.test.ts index 6a73c8008b52e..3da5b2b9b7065 100644 --- a/packages/aws-cdk/test/commands/context-command.test.ts +++ b/packages/aws-cdk/test/commands/context-command.test.ts @@ -1,64 +1,237 @@ import { realHandler } from '../../lib/commands/context'; -import { Configuration } from '../../lib/settings'; +import { Configuration, Settings, Context } from '../../lib/settings'; -test('context list', async() => { - // GIVEN - const configuration = new Configuration(); - configuration.context.set('foo', 'bar'); +describe('context --list', () => { + test('runs', async() => { + // GIVEN + const configuration = new Configuration(); + configuration.context.set('foo', 'bar'); - expect(configuration.context.all).toEqual({ - foo: 'bar', - }); + expect(configuration.context.all).toEqual({ + foo: 'bar', + }); - // WHEN - await realHandler({ - configuration, - args: {}, - } as any); + // WHEN + await realHandler({ + configuration, + args: {}, + } as any); + }); }); -test('context reset can remove a context key', async () => { - // GIVEN - const configuration = new Configuration(); - configuration.context.set('foo', 'bar'); - configuration.context.set('baz', 'quux'); +describe('context --reset', () => { + test('can remove a context key', async () => { + // GIVEN + const configuration = new Configuration(); + configuration.context.set('foo', 'bar'); + configuration.context.set('baz', 'quux'); + + expect(configuration.context.all).toEqual({ + foo: 'bar', + baz: 'quux', + }); - expect(configuration.context.all).toEqual({ - foo: 'bar', - baz: 'quux', + // WHEN + await realHandler({ + configuration, + args: { reset: 'foo' }, + } as any); + + // THEN + expect(configuration.context.all).toEqual({ + baz: 'quux', + }); }); - // WHEN - await realHandler({ - configuration, - args: { reset: 'foo' }, - } as any); + test('can remove a context key using number', async () => { + // GIVEN + const configuration = new Configuration(); + configuration.context.set('foo', 'bar'); + configuration.context.set('baz', 'quux'); + + expect(configuration.context.all).toEqual({ + foo: 'bar', + baz: 'quux', + }); + + // WHEN + await realHandler({ + configuration, + args: { reset: '1' }, + } as any); - // THEN - expect(configuration.context.all).toEqual({ - baz: 'quux', + // THEN + expect(configuration.context.all).toEqual({ + foo: 'bar', + }); }); -}); -test('context reset can remove a context key using number', async () => { - // GIVEN - const configuration = new Configuration(); - configuration.context.set('foo', 'bar'); - configuration.context.set('baz', 'quux'); - expect(configuration.context.all).toEqual({ - foo: 'bar', - baz: 'quux', + test('can reset matched pattern', async () => { + // GIVEN + const configuration = new Configuration(); + configuration.context.set('foo', 'bar'); + configuration.context.set('match-a', 'baz'); + configuration.context.set('match-b', 'qux'); + + expect(configuration.context.all).toEqual({ + 'foo': 'bar', + 'match-a': 'baz', + 'match-b': 'qux', + }); + + // WHEN + await realHandler({ + configuration, + args: { reset: 'match-*' }, + } as any); + + // THEN + expect(configuration.context.all).toEqual({ + foo: 'bar', + }); + }); + + + test('prefers an exact match', async () => { + // GIVEN + const configuration = new Configuration(); + configuration.context.set('foo', 'bar'); + configuration.context.set('fo*', 'baz'); + + expect(configuration.context.all).toEqual({ + 'foo': 'bar', + 'fo*': 'baz', + }); + + // WHEN + await realHandler({ + configuration, + args: { reset: 'fo*' }, + } as any); + + // THEN + expect(configuration.context.all).toEqual({ + foo: 'bar', + }); }); - // WHEN - await realHandler({ - configuration, - args: { reset: '1' }, - } as any); - // THEN - expect(configuration.context.all).toEqual({ - foo: 'bar', + test('doesn\'t throw when at least one match is reset', async () => { + // GIVEN + const configuration = new Configuration(); + const readOnlySettings = new Settings({ + 'foo': 'bar', + 'match-a': 'baz', + }, true); + configuration.context = new Context(readOnlySettings, new Settings()); + configuration.context.set('match-b', 'quux'); + + // When + await expect(realHandler({ + configuration, + args: { reset: 'match-*' }, + } as any)); + + // Then + expect(configuration.context.all).toEqual({ + 'foo': 'bar', + 'match-a': 'baz', + }); + }); + + test('throws when key not found', async () => { + // GIVEN + const configuration = new Configuration(); + configuration.context.set('foo', 'bar'); + + expect(configuration.context.all).toEqual({ + foo: 'bar', + }); + + // THEN + await expect(realHandler({ + configuration, + args: { reset: 'baz' }, + } as any)).rejects.toThrow(/No context value matching key/); }); + + test('Doesn\'t throw when key not found and --force is set', async () => { + // GIVEN + const configuration = new Configuration(); + configuration.context.set('foo', 'bar'); + + expect(configuration.context.all).toEqual({ + foo: 'bar', + }); + + // THEN + await expect(realHandler({ + configuration, + args: { reset: 'baz', force: true }, + } as any)); + }); + + + test('throws when no key of index found', async () => { + // GIVEN + const configuration = new Configuration(); + configuration.context.set('foo', 'bar'); + + expect(configuration.context.all).toEqual({ + foo: 'bar', + }); + + // THEN + await expect(realHandler({ + configuration, + args: { reset: '2' }, + } as any)).rejects.toThrow(/No context key with number/); + }); + + + test('throws when resetting read-only values', async () => { + // GIVEN + const configuration = new Configuration(); + const readOnlySettings = new Settings({ + foo: 'bar', + }, true); + configuration.context = new Context(readOnlySettings); + + expect(configuration.context.all).toEqual({ + foo: 'bar', + }); + + // THEN + await expect(realHandler({ + configuration, + args: { reset: 'foo' }, + } as any)).rejects.toThrow(/Cannot reset readonly context value with key/); + }); + + + test('throws when no matches could be reset', async () => { + // GIVEN + const configuration = new Configuration(); + const readOnlySettings = new Settings({ + 'foo': 'bar', + 'match-a': 'baz', + 'match-b': 'quux', + }, true); + configuration.context = new Context(readOnlySettings); + + expect(configuration.context.all).toEqual({ + 'foo': 'bar', + 'match-a': 'baz', + 'match-b': 'quux', + }); + + // THEN + await expect(realHandler({ + configuration, + args: { reset: 'match-*' }, + } as any)).rejects.toThrow(/None of the matched context values could be reset/); + }); + }); + From 9c0ccca817ace858457717f07c29575cd231a461 Mon Sep 17 00:00:00 2001 From: Tejas M R Date: Thu, 14 Jul 2022 07:00:40 +0530 Subject: [PATCH 40/92] fix(pipelines): reuseCrossRegionSupportStacks=true does not fail when existing pipeline is used (#20423) ---- Added prop check for reuseCrossRegionSupportStacks in CodePipeline which fixes #20334 Added unit tests for all three prop checks. ### All Submissions: * [x] Have you followed the guidelines in our [Contributing guide?](https://github.com/aws/aws-cdk/blob/master/CONTRIBUTING.md) ### Adding new Unconventional Dependencies: * [ ] This PR adds new unconventional dependencies following the process described [here](https://github.com/aws/aws-cdk/blob/master/CONTRIBUTING.md/#adding-new-unconventional-dependencies) ### New Features * [ ] Have you added the new feature to an [integration test](https://github.com/aws/aws-cdk/blob/master/INTEGRATION_TESTS.md)? * [ ] Did you use `yarn integ` to deploy the infrastructure and generate the snapshot (i.e. `yarn integ` without `--dry-run`)? *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- .../lib/codepipeline/codepipeline.ts | 3 + .../test/codepipeline/codepipeline.test.ts | 59 ++++++++++++++++++- 2 files changed, 60 insertions(+), 2 deletions(-) diff --git a/packages/@aws-cdk/pipelines/lib/codepipeline/codepipeline.ts b/packages/@aws-cdk/pipelines/lib/codepipeline/codepipeline.ts index 3eaabf40c6641..6b3dc3ff6d311 100644 --- a/packages/@aws-cdk/pipelines/lib/codepipeline/codepipeline.ts +++ b/packages/@aws-cdk/pipelines/lib/codepipeline/codepipeline.ts @@ -359,6 +359,9 @@ export class CodePipeline extends PipelineBase { if (this.props.crossAccountKeys !== undefined) { throw new Error('Cannot set \'crossAccountKeys\' if an existing CodePipeline is given using \'codePipeline\''); } + if (this.props.reuseCrossRegionSupportStacks !== undefined) { + throw new Error('Cannot set \'reuseCrossRegionSupportStacks\' if an existing CodePipeline is given using \'codePipeline\''); + } this._pipeline = this.props.codePipeline; } else { diff --git a/packages/@aws-cdk/pipelines/test/codepipeline/codepipeline.test.ts b/packages/@aws-cdk/pipelines/test/codepipeline/codepipeline.test.ts index 4d3b411fe60c9..8a6efac975888 100644 --- a/packages/@aws-cdk/pipelines/test/codepipeline/codepipeline.test.ts +++ b/packages/@aws-cdk/pipelines/test/codepipeline/codepipeline.test.ts @@ -1,5 +1,6 @@ import { Template, Annotations, Match } from '@aws-cdk/assertions'; import * as ccommit from '@aws-cdk/aws-codecommit'; +import { Pipeline } from '@aws-cdk/aws-codepipeline'; import * as sqs from '@aws-cdk/aws-sqs'; import * as cdk from '@aws-cdk/core'; import { Construct } from 'constructs'; @@ -71,6 +72,24 @@ describe('CodePipeline support stack reuse', () => { }); }); +describe('Providing codePipeline parameter and prop(s) of codePipeline parameter to CodePipeline constructor should throw error', () => { + test('Providing codePipeline parameter and pipelineName parameter should throw error', () => { + expect(() => new CodePipelinePropsCheckTest(app, 'CodePipeline', { + pipelineName: 'randomName', + }).create()).toThrowError('Cannot set \'pipelineName\' if an existing CodePipeline is given using \'codePipeline\''); + }); + test('Providing codePipeline parameter and crossAccountKeys parameter should throw error', () => { + expect(() => new CodePipelinePropsCheckTest(app, 'CodePipeline', { + crossAccountKeys: true, + }).create()).toThrowError('Cannot set \'crossAccountKeys\' if an existing CodePipeline is given using \'codePipeline\''); + }); + test('Providing codePipeline parameter and reuseCrossRegionSupportStacks parameter should throw error', () => { + expect(() => new CodePipelinePropsCheckTest(app, 'CodePipeline', { + reuseCrossRegionSupportStacks: true, + }).create()).toThrowError('Cannot set \'reuseCrossRegionSupportStacks\' if an existing CodePipeline is given using \'codePipeline\''); + }); +}); + test('Policy sizes do not exceed the maximum size', () => { const pipelineStack = new cdk.Stack(app, 'PipelineStack', { env: PIPELINE_ENV }); pipelineStack.node.setContext('@aws-cdk/aws-iam:minimizePolicies', true); @@ -112,7 +131,6 @@ test('Policy sizes do not exceed the maximum size', () => { } } - // Validate sizes // // Not entirely accurate, because our "Ref"s and "Fn::GetAtt"s actually need to be evaluated @@ -217,4 +235,41 @@ class ReuseStack extends cdk.Stack { super(scope, id, props); new sqs.Queue(this, 'Queue'); } -} \ No newline at end of file +} + +interface CodePipelineStackProps extends cdk.StackProps { + pipelineName?: string; + crossAccountKeys?: boolean; + reuseCrossRegionSupportStacks?: boolean; +} + +class CodePipelinePropsCheckTest extends cdk.Stack { + cProps: CodePipelineStackProps; + public constructor(scope: Construct, id: string, props: CodePipelineStackProps) { + super(scope, id, props); + this.cProps = props; + } + public create() { + if (this.cProps.pipelineName !== undefined) { + new cdkp.CodePipeline(this, 'CodePipeline1', { + pipelineName: this.cProps.pipelineName, + codePipeline: new Pipeline(this, 'Pipeline1'), + synth: new cdkp.ShellStep('Synth', { commands: ['ls'] }), + }).buildPipeline(); + } + if (this.cProps.crossAccountKeys !== undefined) { + new cdkp.CodePipeline(this, 'CodePipeline2', { + crossAccountKeys: this.cProps.crossAccountKeys, + codePipeline: new Pipeline(this, 'Pipline2'), + synth: new cdkp.ShellStep('Synth', { commands: ['ls'] }), + }).buildPipeline(); + } + if (this.cProps.reuseCrossRegionSupportStacks !== undefined) { + new cdkp.CodePipeline(this, 'CodePipeline3', { + reuseCrossRegionSupportStacks: this.cProps.reuseCrossRegionSupportStacks, + codePipeline: new Pipeline(this, 'Pipline3'), + synth: new cdkp.ShellStep('Synth', { commands: ['ls'] }), + }).buildPipeline(); + } + } +} From c1495f0b700cedc04b556844397048ee41a7d891 Mon Sep 17 00:00:00 2001 From: Robert Koch Date: Thu, 14 Jul 2022 10:07:02 +0800 Subject: [PATCH 41/92] fix(appsync): domain name api association fails when domain name creation is in the same stack (#20173) Fixes #18395 and other issues where defining domain name in `aws-appsync-alpha` will fail because domain name is not created before the domain name association. ---- ### All Submissions: * [x] Have you followed the guidelines in our [Contributing guide?](https://github.com/aws/aws-cdk/blob/master/CONTRIBUTING.md) ### Adding new Unconventional Dependencies: * [ ] This PR adds new unconventional dependencies following the process described [here](https://github.com/aws/aws-cdk/blob/master/CONTRIBUTING.md/#adding-new-unconventional-dependencies) ### New Features * [ ] Have you added the new feature to an [integration test](https://github.com/aws/aws-cdk/blob/master/INTEGRATION_TESTS.md)? * [ ] Did you use `yarn integ` to deploy the infrastructure and generate the snapshot (i.e. `yarn integ` without `--dry-run`)? *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- .../@aws-cdk/aws-appsync/lib/graphqlapi.ts | 6 ++- .../aws-appsync/test/appsync-domain.test.ts | 43 +++++++++++++++++++ 2 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 packages/@aws-cdk/aws-appsync/test/appsync-domain.test.ts diff --git a/packages/@aws-cdk/aws-appsync/lib/graphqlapi.ts b/packages/@aws-cdk/aws-appsync/lib/graphqlapi.ts index 3dab9518ed3df..ab740aac4cfb2 100644 --- a/packages/@aws-cdk/aws-appsync/lib/graphqlapi.ts +++ b/packages/@aws-cdk/aws-appsync/lib/graphqlapi.ts @@ -494,16 +494,18 @@ export class GraphqlApi extends GraphqlApiBase { this.schemaResource = this.schema.bind(this); if (props.domainName) { - new CfnDomainName(this, 'DomainName', { + const domainName = new CfnDomainName(this, 'DomainName', { domainName: props.domainName.domainName, certificateArn: props.domainName.certificate.certificateArn, description: `domain for ${this.name} at ${this.graphqlUrl}`, }); - new CfnDomainNameApiAssociation(this, 'DomainAssociation', { + const domainNameAssociation = new CfnDomainNameApiAssociation(this, 'DomainAssociation', { domainName: props.domainName.domainName, apiId: this.apiId, }); + + domainNameAssociation.addDependsOn(domainName); } if (modes.some((mode) => mode.authorizationType === AuthorizationType.API_KEY)) { diff --git a/packages/@aws-cdk/aws-appsync/test/appsync-domain.test.ts b/packages/@aws-cdk/aws-appsync/test/appsync-domain.test.ts new file mode 100644 index 0000000000000..6e445085e4264 --- /dev/null +++ b/packages/@aws-cdk/aws-appsync/test/appsync-domain.test.ts @@ -0,0 +1,43 @@ +import * as path from 'path'; +import { Template } from '@aws-cdk/assertions'; +import * as acm from '@aws-cdk/aws-certificatemanager'; +import { Certificate } from '@aws-cdk/aws-certificatemanager'; +import * as cdk from '@aws-cdk/core'; +import * as appsync from '../lib'; + +// GLOBAL GIVEN +let stack: cdk.Stack; +let certificate: acm.Certificate; + +beforeEach(() => { + stack = new cdk.Stack(); + certificate = new Certificate(stack, 'certificate', { + domainName: 'aws.amazon.com', + }); +}); + +describe('Tests of AppSync Domain Name', () => { + test('DomainNameAssociation depends on DomainName construct', () => { + new appsync.GraphqlApi(stack, 'baseApi', { + name: 'api', + schema: appsync.Schema.fromAsset( + path.join(__dirname, 'appsync.test.graphql'), + ), + domainName: { + certificate, + domainName: 'aws.amazon.com', + }, + }); + + const domainName = Template.fromStack(stack).findResources( + 'AWS::AppSync::DomainName', + ); + + Template.fromStack(stack).hasResource( + 'AWS::AppSync::DomainNameApiAssociation', + { + DependsOn: [Object.keys(domainName)[0]], + }, + ); + }); +}); From 6640338823738017b35cdaa391243aa5782e0bcf Mon Sep 17 00:00:00 2001 From: Jonathan Goldwasser Date: Thu, 14 Jul 2022 04:42:43 +0200 Subject: [PATCH 42/92] fix(apigateway): serialization exception with step functions integration (#20169) Using single quotes (`'`) in the body results in a serialization exception from API Gateway (`com.amazon.coral.service#SerializationException`). This is because `$util.escapeJavaScript()` turns regular single quotes into escaped ones that are not valid in JSON. The solution is to replace escaped single quotes back to regular single quotes after escaping the string. See https://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-mapping-template-reference.html#util-template-reference ![image](https://user-images.githubusercontent.com/12623249/166241425-e7a2462d-c4ef-474d-b676-123f479cfdd0.png) ---- ### All Submissions: * [x] Have you followed the guidelines in our [Contributing guide?](https://github.com/aws/aws-cdk/blob/master/CONTRIBUTING.md) ### Adding new Unconventional Dependencies: * [ ] This PR adds new unconventional dependencies following the process described [here](https://github.com/aws/aws-cdk/blob/master/CONTRIBUTING.md/#adding-new-unconventional-dependencies) ### New Features * [ ] Have you added the new feature to an [integration test](https://github.com/aws/aws-cdk/blob/master/INTEGRATION_TESTS.md)? * [ ] Did you use `yarn integ` to deploy the infrastructure and generate the snapshot (i.e. `yarn integ` without `--dry-run`)? *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- .../aws-apigateway/lib/integrations/stepfunctions.vtl | 2 +- .../aws-apigateway/test/integrations/stepfunctions.test.ts | 2 +- .../StepFunctionsRestApiDeploymentStack.template.json | 2 +- .../test/stepfunctions-api.integ.snapshot/cdk.out | 2 +- .../test/stepfunctions-api.integ.snapshot/integ.json | 2 +- .../test/stepfunctions-api.integ.snapshot/manifest.json | 2 +- .../test/stepfunctions-api.integ.snapshot/tree.json | 2 +- .../@aws-cdk/aws-apigateway/test/stepfunctions-api.test.ts | 4 ++-- 8 files changed, 9 insertions(+), 9 deletions(-) diff --git a/packages/@aws-cdk/aws-apigateway/lib/integrations/stepfunctions.vtl b/packages/@aws-cdk/aws-apigateway/lib/integrations/stepfunctions.vtl index f389662efd6ed..c297264b80aa8 100644 --- a/packages/@aws-cdk/aws-apigateway/lib/integrations/stepfunctions.vtl +++ b/packages/@aws-cdk/aws-apigateway/lib/integrations/stepfunctions.vtl @@ -71,5 +71,5 @@ #set($inputString = "$inputString}") #set($inputString = $inputString.replaceAll("@@",'"')) #set($len = $inputString.length() - 1) - "input": "{$util.escapeJavaScript($inputString.substring(1,$len))}" + "input": "{$util.escapeJavaScript($inputString.substring(1,$len)).replaceAll("\\'","'")}" } diff --git a/packages/@aws-cdk/aws-apigateway/test/integrations/stepfunctions.test.ts b/packages/@aws-cdk/aws-apigateway/test/integrations/stepfunctions.test.ts index 32fd13c5741c0..01c5320cae179 100644 --- a/packages/@aws-cdk/aws-apigateway/test/integrations/stepfunctions.test.ts +++ b/packages/@aws-cdk/aws-apigateway/test/integrations/stepfunctions.test.ts @@ -56,7 +56,7 @@ describe('StepFunctionsIntegration', () => { { Ref: 'StateMachine2E01A3A5', }, - "\",\n\n #set($inputString = \"$inputString,@@body@@: $input.body\")\n\n #if ($includeHeaders)\n #set($inputString = \"$inputString, @@header@@:{\")\n #foreach($paramName in $allParams.header.keySet())\n #set($inputString = \"$inputString @@$paramName@@: @@$util.escapeJavaScript($allParams.header.get($paramName))@@\")\n #if($foreach.hasNext)\n #set($inputString = \"$inputString,\")\n #end\n #end\n #set($inputString = \"$inputString }\")\n \n #end\n\n #if ($includeQueryString)\n #set($inputString = \"$inputString, @@querystring@@:{\")\n #foreach($paramName in $allParams.querystring.keySet())\n #set($inputString = \"$inputString @@$paramName@@: @@$util.escapeJavaScript($allParams.querystring.get($paramName))@@\")\n #if($foreach.hasNext)\n #set($inputString = \"$inputString,\")\n #end\n #end\n #set($inputString = \"$inputString }\")\n #end\n\n #if ($includePath)\n #set($inputString = \"$inputString, @@path@@:{\")\n #foreach($paramName in $allParams.path.keySet())\n #set($inputString = \"$inputString @@$paramName@@: @@$util.escapeJavaScript($allParams.path.get($paramName))@@\")\n #if($foreach.hasNext)\n #set($inputString = \"$inputString,\")\n #end\n #end\n #set($inputString = \"$inputString }\")\n #end\n \n #if ($includeAuthorizer)\n #set($inputString = \"$inputString, @@authorizer@@:{\")\n #foreach($paramName in $context.authorizer.keySet())\n #set($inputString = \"$inputString @@$paramName@@: @@$util.escapeJavaScript($context.authorizer.get($paramName))@@\")\n #if($foreach.hasNext)\n #set($inputString = \"$inputString,\")\n #end\n #end\n #set($inputString = \"$inputString }\")\n #end\n\n #set($requestContext = \"\")\n ## Check if the request context should be included as part of the execution input\n #if($requestContext && !$requestContext.empty)\n #set($inputString = \"$inputString,\")\n #set($inputString = \"$inputString @@requestContext@@: $requestContext\")\n #end\n\n #set($inputString = \"$inputString}\")\n #set($inputString = $inputString.replaceAll(\"@@\",'\"'))\n #set($len = $inputString.length() - 1)\n \"input\": \"{$util.escapeJavaScript($inputString.substring(1,$len))}\"\n}\n", + "\",\n\n #set($inputString = \"$inputString,@@body@@: $input.body\")\n\n #if ($includeHeaders)\n #set($inputString = \"$inputString, @@header@@:{\")\n #foreach($paramName in $allParams.header.keySet())\n #set($inputString = \"$inputString @@$paramName@@: @@$util.escapeJavaScript($allParams.header.get($paramName))@@\")\n #if($foreach.hasNext)\n #set($inputString = \"$inputString,\")\n #end\n #end\n #set($inputString = \"$inputString }\")\n \n #end\n\n #if ($includeQueryString)\n #set($inputString = \"$inputString, @@querystring@@:{\")\n #foreach($paramName in $allParams.querystring.keySet())\n #set($inputString = \"$inputString @@$paramName@@: @@$util.escapeJavaScript($allParams.querystring.get($paramName))@@\")\n #if($foreach.hasNext)\n #set($inputString = \"$inputString,\")\n #end\n #end\n #set($inputString = \"$inputString }\")\n #end\n\n #if ($includePath)\n #set($inputString = \"$inputString, @@path@@:{\")\n #foreach($paramName in $allParams.path.keySet())\n #set($inputString = \"$inputString @@$paramName@@: @@$util.escapeJavaScript($allParams.path.get($paramName))@@\")\n #if($foreach.hasNext)\n #set($inputString = \"$inputString,\")\n #end\n #end\n #set($inputString = \"$inputString }\")\n #end\n \n #if ($includeAuthorizer)\n #set($inputString = \"$inputString, @@authorizer@@:{\")\n #foreach($paramName in $context.authorizer.keySet())\n #set($inputString = \"$inputString @@$paramName@@: @@$util.escapeJavaScript($context.authorizer.get($paramName))@@\")\n #if($foreach.hasNext)\n #set($inputString = \"$inputString,\")\n #end\n #end\n #set($inputString = \"$inputString }\")\n #end\n\n #set($requestContext = \"\")\n ## Check if the request context should be included as part of the execution input\n #if($requestContext && !$requestContext.empty)\n #set($inputString = \"$inputString,\")\n #set($inputString = \"$inputString @@requestContext@@: $requestContext\")\n #end\n\n #set($inputString = \"$inputString}\")\n #set($inputString = $inputString.replaceAll(\"@@\",'\"'))\n #set($len = $inputString.length() - 1)\n \"input\": \"{$util.escapeJavaScript($inputString.substring(1,$len)).replaceAll(\"\\\\'\",\"'\")}\"\n}\n", ], ], }, diff --git a/packages/@aws-cdk/aws-apigateway/test/stepfunctions-api.integ.snapshot/StepFunctionsRestApiDeploymentStack.template.json b/packages/@aws-cdk/aws-apigateway/test/stepfunctions-api.integ.snapshot/StepFunctionsRestApiDeploymentStack.template.json index 4c898742cde37..2b3e23721ccb5 100644 --- a/packages/@aws-cdk/aws-apigateway/test/stepfunctions-api.integ.snapshot/StepFunctionsRestApiDeploymentStack.template.json +++ b/packages/@aws-cdk/aws-apigateway/test/stepfunctions-api.integ.snapshot/StepFunctionsRestApiDeploymentStack.template.json @@ -186,7 +186,7 @@ { "Ref": "StateMachine2E01A3A5" }, - "\",\n\n #set($inputString = \"$inputString,@@body@@: $input.body\")\n\n #if ($includeHeaders)\n #set($inputString = \"$inputString, @@header@@:{\")\n #foreach($paramName in $allParams.header.keySet())\n #set($inputString = \"$inputString @@$paramName@@: @@$util.escapeJavaScript($allParams.header.get($paramName))@@\")\n #if($foreach.hasNext)\n #set($inputString = \"$inputString,\")\n #end\n #end\n #set($inputString = \"$inputString }\")\n \n #end\n\n #if ($includeQueryString)\n #set($inputString = \"$inputString, @@querystring@@:{\")\n #foreach($paramName in $allParams.querystring.keySet())\n #set($inputString = \"$inputString @@$paramName@@: @@$util.escapeJavaScript($allParams.querystring.get($paramName))@@\")\n #if($foreach.hasNext)\n #set($inputString = \"$inputString,\")\n #end\n #end\n #set($inputString = \"$inputString }\")\n #end\n\n #if ($includePath)\n #set($inputString = \"$inputString, @@path@@:{\")\n #foreach($paramName in $allParams.path.keySet())\n #set($inputString = \"$inputString @@$paramName@@: @@$util.escapeJavaScript($allParams.path.get($paramName))@@\")\n #if($foreach.hasNext)\n #set($inputString = \"$inputString,\")\n #end\n #end\n #set($inputString = \"$inputString }\")\n #end\n \n #if ($includeAuthorizer)\n #set($inputString = \"$inputString, @@authorizer@@:{\")\n #foreach($paramName in $context.authorizer.keySet())\n #set($inputString = \"$inputString @@$paramName@@: @@$util.escapeJavaScript($context.authorizer.get($paramName))@@\")\n #if($foreach.hasNext)\n #set($inputString = \"$inputString,\")\n #end\n #end\n #set($inputString = \"$inputString }\")\n #end\n\n #set($requestContext = \"{@@accountId@@:@@$context.identity.accountId@@,@@userArn@@:@@$context.identity.userArn@@}\")\n ## Check if the request context should be included as part of the execution input\n #if($requestContext && !$requestContext.empty)\n #set($inputString = \"$inputString,\")\n #set($inputString = \"$inputString @@requestContext@@: $requestContext\")\n #end\n\n #set($inputString = \"$inputString}\")\n #set($inputString = $inputString.replaceAll(\"@@\",'\"'))\n #set($len = $inputString.length() - 1)\n \"input\": \"{$util.escapeJavaScript($inputString.substring(1,$len))}\"\n}\n" + "\",\n\n #set($inputString = \"$inputString,@@body@@: $input.body\")\n\n #if ($includeHeaders)\n #set($inputString = \"$inputString, @@header@@:{\")\n #foreach($paramName in $allParams.header.keySet())\n #set($inputString = \"$inputString @@$paramName@@: @@$util.escapeJavaScript($allParams.header.get($paramName))@@\")\n #if($foreach.hasNext)\n #set($inputString = \"$inputString,\")\n #end\n #end\n #set($inputString = \"$inputString }\")\n \n #end\n\n #if ($includeQueryString)\n #set($inputString = \"$inputString, @@querystring@@:{\")\n #foreach($paramName in $allParams.querystring.keySet())\n #set($inputString = \"$inputString @@$paramName@@: @@$util.escapeJavaScript($allParams.querystring.get($paramName))@@\")\n #if($foreach.hasNext)\n #set($inputString = \"$inputString,\")\n #end\n #end\n #set($inputString = \"$inputString }\")\n #end\n\n #if ($includePath)\n #set($inputString = \"$inputString, @@path@@:{\")\n #foreach($paramName in $allParams.path.keySet())\n #set($inputString = \"$inputString @@$paramName@@: @@$util.escapeJavaScript($allParams.path.get($paramName))@@\")\n #if($foreach.hasNext)\n #set($inputString = \"$inputString,\")\n #end\n #end\n #set($inputString = \"$inputString }\")\n #end\n \n #if ($includeAuthorizer)\n #set($inputString = \"$inputString, @@authorizer@@:{\")\n #foreach($paramName in $context.authorizer.keySet())\n #set($inputString = \"$inputString @@$paramName@@: @@$util.escapeJavaScript($context.authorizer.get($paramName))@@\")\n #if($foreach.hasNext)\n #set($inputString = \"$inputString,\")\n #end\n #end\n #set($inputString = \"$inputString }\")\n #end\n\n #set($requestContext = \"{@@accountId@@:@@$context.identity.accountId@@,@@userArn@@:@@$context.identity.userArn@@}\")\n ## Check if the request context should be included as part of the execution input\n #if($requestContext && !$requestContext.empty)\n #set($inputString = \"$inputString,\")\n #set($inputString = \"$inputString @@requestContext@@: $requestContext\")\n #end\n\n #set($inputString = \"$inputString}\")\n #set($inputString = $inputString.replaceAll(\"@@\",'\"'))\n #set($len = $inputString.length() - 1)\n \"input\": \"{$util.escapeJavaScript($inputString.substring(1,$len)).replaceAll(\"\\\\'\",\"'\")}\"\n}\n" ] ] } diff --git a/packages/@aws-cdk/aws-apigateway/test/stepfunctions-api.integ.snapshot/cdk.out b/packages/@aws-cdk/aws-apigateway/test/stepfunctions-api.integ.snapshot/cdk.out index 90bef2e09ad39..2efc89439fab8 100644 --- a/packages/@aws-cdk/aws-apigateway/test/stepfunctions-api.integ.snapshot/cdk.out +++ b/packages/@aws-cdk/aws-apigateway/test/stepfunctions-api.integ.snapshot/cdk.out @@ -1 +1 @@ -{"version":"17.0.0"} \ No newline at end of file +{"version":"18.0.0"} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-apigateway/test/stepfunctions-api.integ.snapshot/integ.json b/packages/@aws-cdk/aws-apigateway/test/stepfunctions-api.integ.snapshot/integ.json index ce0eb287e42d4..041943bc2856c 100644 --- a/packages/@aws-cdk/aws-apigateway/test/stepfunctions-api.integ.snapshot/integ.json +++ b/packages/@aws-cdk/aws-apigateway/test/stepfunctions-api.integ.snapshot/integ.json @@ -1,7 +1,7 @@ { "version": "18.0.0", "testCases": { - "aws-apigateway/test/integ.stepfunctions-api": { + "integ.stepfunctions-api": { "stacks": [ "StepFunctionsRestApiDeploymentStack" ], diff --git a/packages/@aws-cdk/aws-apigateway/test/stepfunctions-api.integ.snapshot/manifest.json b/packages/@aws-cdk/aws-apigateway/test/stepfunctions-api.integ.snapshot/manifest.json index a9a218710a685..ae33bd6923c7c 100644 --- a/packages/@aws-cdk/aws-apigateway/test/stepfunctions-api.integ.snapshot/manifest.json +++ b/packages/@aws-cdk/aws-apigateway/test/stepfunctions-api.integ.snapshot/manifest.json @@ -1,5 +1,5 @@ { - "version": "17.0.0", + "version": "18.0.0", "artifacts": { "Tree": { "type": "cdk:tree", diff --git a/packages/@aws-cdk/aws-apigateway/test/stepfunctions-api.integ.snapshot/tree.json b/packages/@aws-cdk/aws-apigateway/test/stepfunctions-api.integ.snapshot/tree.json index 9681e397db30e..5f471045630af 100644 --- a/packages/@aws-cdk/aws-apigateway/test/stepfunctions-api.integ.snapshot/tree.json +++ b/packages/@aws-cdk/aws-apigateway/test/stepfunctions-api.integ.snapshot/tree.json @@ -313,7 +313,7 @@ { "Ref": "StateMachine2E01A3A5" }, - "\",\n\n #set($inputString = \"$inputString,@@body@@: $input.body\")\n\n #if ($includeHeaders)\n #set($inputString = \"$inputString, @@header@@:{\")\n #foreach($paramName in $allParams.header.keySet())\n #set($inputString = \"$inputString @@$paramName@@: @@$util.escapeJavaScript($allParams.header.get($paramName))@@\")\n #if($foreach.hasNext)\n #set($inputString = \"$inputString,\")\n #end\n #end\n #set($inputString = \"$inputString }\")\n \n #end\n\n #if ($includeQueryString)\n #set($inputString = \"$inputString, @@querystring@@:{\")\n #foreach($paramName in $allParams.querystring.keySet())\n #set($inputString = \"$inputString @@$paramName@@: @@$util.escapeJavaScript($allParams.querystring.get($paramName))@@\")\n #if($foreach.hasNext)\n #set($inputString = \"$inputString,\")\n #end\n #end\n #set($inputString = \"$inputString }\")\n #end\n\n #if ($includePath)\n #set($inputString = \"$inputString, @@path@@:{\")\n #foreach($paramName in $allParams.path.keySet())\n #set($inputString = \"$inputString @@$paramName@@: @@$util.escapeJavaScript($allParams.path.get($paramName))@@\")\n #if($foreach.hasNext)\n #set($inputString = \"$inputString,\")\n #end\n #end\n #set($inputString = \"$inputString }\")\n #end\n \n #if ($includeAuthorizer)\n #set($inputString = \"$inputString, @@authorizer@@:{\")\n #foreach($paramName in $context.authorizer.keySet())\n #set($inputString = \"$inputString @@$paramName@@: @@$util.escapeJavaScript($context.authorizer.get($paramName))@@\")\n #if($foreach.hasNext)\n #set($inputString = \"$inputString,\")\n #end\n #end\n #set($inputString = \"$inputString }\")\n #end\n\n #set($requestContext = \"{@@accountId@@:@@$context.identity.accountId@@,@@userArn@@:@@$context.identity.userArn@@}\")\n ## Check if the request context should be included as part of the execution input\n #if($requestContext && !$requestContext.empty)\n #set($inputString = \"$inputString,\")\n #set($inputString = \"$inputString @@requestContext@@: $requestContext\")\n #end\n\n #set($inputString = \"$inputString}\")\n #set($inputString = $inputString.replaceAll(\"@@\",'\"'))\n #set($len = $inputString.length() - 1)\n \"input\": \"{$util.escapeJavaScript($inputString.substring(1,$len))}\"\n}\n" + "\",\n\n #set($inputString = \"$inputString,@@body@@: $input.body\")\n\n #if ($includeHeaders)\n #set($inputString = \"$inputString, @@header@@:{\")\n #foreach($paramName in $allParams.header.keySet())\n #set($inputString = \"$inputString @@$paramName@@: @@$util.escapeJavaScript($allParams.header.get($paramName))@@\")\n #if($foreach.hasNext)\n #set($inputString = \"$inputString,\")\n #end\n #end\n #set($inputString = \"$inputString }\")\n \n #end\n\n #if ($includeQueryString)\n #set($inputString = \"$inputString, @@querystring@@:{\")\n #foreach($paramName in $allParams.querystring.keySet())\n #set($inputString = \"$inputString @@$paramName@@: @@$util.escapeJavaScript($allParams.querystring.get($paramName))@@\")\n #if($foreach.hasNext)\n #set($inputString = \"$inputString,\")\n #end\n #end\n #set($inputString = \"$inputString }\")\n #end\n\n #if ($includePath)\n #set($inputString = \"$inputString, @@path@@:{\")\n #foreach($paramName in $allParams.path.keySet())\n #set($inputString = \"$inputString @@$paramName@@: @@$util.escapeJavaScript($allParams.path.get($paramName))@@\")\n #if($foreach.hasNext)\n #set($inputString = \"$inputString,\")\n #end\n #end\n #set($inputString = \"$inputString }\")\n #end\n \n #if ($includeAuthorizer)\n #set($inputString = \"$inputString, @@authorizer@@:{\")\n #foreach($paramName in $context.authorizer.keySet())\n #set($inputString = \"$inputString @@$paramName@@: @@$util.escapeJavaScript($context.authorizer.get($paramName))@@\")\n #if($foreach.hasNext)\n #set($inputString = \"$inputString,\")\n #end\n #end\n #set($inputString = \"$inputString }\")\n #end\n\n #set($requestContext = \"{@@accountId@@:@@$context.identity.accountId@@,@@userArn@@:@@$context.identity.userArn@@}\")\n ## Check if the request context should be included as part of the execution input\n #if($requestContext && !$requestContext.empty)\n #set($inputString = \"$inputString,\")\n #set($inputString = \"$inputString @@requestContext@@: $requestContext\")\n #end\n\n #set($inputString = \"$inputString}\")\n #set($inputString = $inputString.replaceAll(\"@@\",'\"'))\n #set($len = $inputString.length() - 1)\n \"input\": \"{$util.escapeJavaScript($inputString.substring(1,$len)).replaceAll(\"\\\\'\",\"'\")}\"\n}\n" ] ] } diff --git a/packages/@aws-cdk/aws-apigateway/test/stepfunctions-api.test.ts b/packages/@aws-cdk/aws-apigateway/test/stepfunctions-api.test.ts index 44643ea0814ff..c4c826b13d1bd 100644 --- a/packages/@aws-cdk/aws-apigateway/test/stepfunctions-api.test.ts +++ b/packages/@aws-cdk/aws-apigateway/test/stepfunctions-api.test.ts @@ -49,7 +49,7 @@ describe('Step Functions api', () => { { Ref: 'StateMachine2E01A3A5', }, - "\",\n\n #set($inputString = \"$inputString,@@body@@: $input.body\")\n\n #if ($includeHeaders)\n #set($inputString = \"$inputString, @@header@@:{\")\n #foreach($paramName in $allParams.header.keySet())\n #set($inputString = \"$inputString @@$paramName@@: @@$util.escapeJavaScript($allParams.header.get($paramName))@@\")\n #if($foreach.hasNext)\n #set($inputString = \"$inputString,\")\n #end\n #end\n #set($inputString = \"$inputString }\")\n \n #end\n\n #if ($includeQueryString)\n #set($inputString = \"$inputString, @@querystring@@:{\")\n #foreach($paramName in $allParams.querystring.keySet())\n #set($inputString = \"$inputString @@$paramName@@: @@$util.escapeJavaScript($allParams.querystring.get($paramName))@@\")\n #if($foreach.hasNext)\n #set($inputString = \"$inputString,\")\n #end\n #end\n #set($inputString = \"$inputString }\")\n #end\n\n #if ($includePath)\n #set($inputString = \"$inputString, @@path@@:{\")\n #foreach($paramName in $allParams.path.keySet())\n #set($inputString = \"$inputString @@$paramName@@: @@$util.escapeJavaScript($allParams.path.get($paramName))@@\")\n #if($foreach.hasNext)\n #set($inputString = \"$inputString,\")\n #end\n #end\n #set($inputString = \"$inputString }\")\n #end\n \n #if ($includeAuthorizer)\n #set($inputString = \"$inputString, @@authorizer@@:{\")\n #foreach($paramName in $context.authorizer.keySet())\n #set($inputString = \"$inputString @@$paramName@@: @@$util.escapeJavaScript($context.authorizer.get($paramName))@@\")\n #if($foreach.hasNext)\n #set($inputString = \"$inputString,\")\n #end\n #end\n #set($inputString = \"$inputString }\")\n #end\n\n #set($requestContext = \"\")\n ## Check if the request context should be included as part of the execution input\n #if($requestContext && !$requestContext.empty)\n #set($inputString = \"$inputString,\")\n #set($inputString = \"$inputString @@requestContext@@: $requestContext\")\n #end\n\n #set($inputString = \"$inputString}\")\n #set($inputString = $inputString.replaceAll(\"@@\",'\"'))\n #set($len = $inputString.length() - 1)\n \"input\": \"{$util.escapeJavaScript($inputString.substring(1,$len))}\"\n}\n", + "\",\n\n #set($inputString = \"$inputString,@@body@@: $input.body\")\n\n #if ($includeHeaders)\n #set($inputString = \"$inputString, @@header@@:{\")\n #foreach($paramName in $allParams.header.keySet())\n #set($inputString = \"$inputString @@$paramName@@: @@$util.escapeJavaScript($allParams.header.get($paramName))@@\")\n #if($foreach.hasNext)\n #set($inputString = \"$inputString,\")\n #end\n #end\n #set($inputString = \"$inputString }\")\n \n #end\n\n #if ($includeQueryString)\n #set($inputString = \"$inputString, @@querystring@@:{\")\n #foreach($paramName in $allParams.querystring.keySet())\n #set($inputString = \"$inputString @@$paramName@@: @@$util.escapeJavaScript($allParams.querystring.get($paramName))@@\")\n #if($foreach.hasNext)\n #set($inputString = \"$inputString,\")\n #end\n #end\n #set($inputString = \"$inputString }\")\n #end\n\n #if ($includePath)\n #set($inputString = \"$inputString, @@path@@:{\")\n #foreach($paramName in $allParams.path.keySet())\n #set($inputString = \"$inputString @@$paramName@@: @@$util.escapeJavaScript($allParams.path.get($paramName))@@\")\n #if($foreach.hasNext)\n #set($inputString = \"$inputString,\")\n #end\n #end\n #set($inputString = \"$inputString }\")\n #end\n \n #if ($includeAuthorizer)\n #set($inputString = \"$inputString, @@authorizer@@:{\")\n #foreach($paramName in $context.authorizer.keySet())\n #set($inputString = \"$inputString @@$paramName@@: @@$util.escapeJavaScript($context.authorizer.get($paramName))@@\")\n #if($foreach.hasNext)\n #set($inputString = \"$inputString,\")\n #end\n #end\n #set($inputString = \"$inputString }\")\n #end\n\n #set($requestContext = \"\")\n ## Check if the request context should be included as part of the execution input\n #if($requestContext && !$requestContext.empty)\n #set($inputString = \"$inputString,\")\n #set($inputString = \"$inputString @@requestContext@@: $requestContext\")\n #end\n\n #set($inputString = \"$inputString}\")\n #set($inputString = $inputString.replaceAll(\"@@\",'\"'))\n #set($len = $inputString.length() - 1)\n \"input\": \"{$util.escapeJavaScript($inputString.substring(1,$len)).replaceAll(\"\\\\'\",\"'\")}\"\n}\n", ], ], }, @@ -110,7 +110,7 @@ describe('Step Functions api', () => { { Ref: 'StateMachine2E01A3A5', }, - "\",\n\n #set($inputString = \"$inputString,@@body@@: $input.body\")\n\n #if ($includeHeaders)\n #set($inputString = \"$inputString, @@header@@:{\")\n #foreach($paramName in $allParams.header.keySet())\n #set($inputString = \"$inputString @@$paramName@@: @@$util.escapeJavaScript($allParams.header.get($paramName))@@\")\n #if($foreach.hasNext)\n #set($inputString = \"$inputString,\")\n #end\n #end\n #set($inputString = \"$inputString }\")\n \n #end\n\n #if ($includeQueryString)\n #set($inputString = \"$inputString, @@querystring@@:{\")\n #foreach($paramName in $allParams.querystring.keySet())\n #set($inputString = \"$inputString @@$paramName@@: @@$util.escapeJavaScript($allParams.querystring.get($paramName))@@\")\n #if($foreach.hasNext)\n #set($inputString = \"$inputString,\")\n #end\n #end\n #set($inputString = \"$inputString }\")\n #end\n\n #if ($includePath)\n #set($inputString = \"$inputString, @@path@@:{\")\n #foreach($paramName in $allParams.path.keySet())\n #set($inputString = \"$inputString @@$paramName@@: @@$util.escapeJavaScript($allParams.path.get($paramName))@@\")\n #if($foreach.hasNext)\n #set($inputString = \"$inputString,\")\n #end\n #end\n #set($inputString = \"$inputString }\")\n #end\n \n #if ($includeAuthorizer)\n #set($inputString = \"$inputString, @@authorizer@@:{\")\n #foreach($paramName in $context.authorizer.keySet())\n #set($inputString = \"$inputString @@$paramName@@: @@$util.escapeJavaScript($context.authorizer.get($paramName))@@\")\n #if($foreach.hasNext)\n #set($inputString = \"$inputString,\")\n #end\n #end\n #set($inputString = \"$inputString }\")\n #end\n\n #set($requestContext = \"\")\n ## Check if the request context should be included as part of the execution input\n #if($requestContext && !$requestContext.empty)\n #set($inputString = \"$inputString,\")\n #set($inputString = \"$inputString @@requestContext@@: $requestContext\")\n #end\n\n #set($inputString = \"$inputString}\")\n #set($inputString = $inputString.replaceAll(\"@@\",'\"'))\n #set($len = $inputString.length() - 1)\n \"input\": \"{$util.escapeJavaScript($inputString.substring(1,$len))}\"\n}\n", + "\",\n\n #set($inputString = \"$inputString,@@body@@: $input.body\")\n\n #if ($includeHeaders)\n #set($inputString = \"$inputString, @@header@@:{\")\n #foreach($paramName in $allParams.header.keySet())\n #set($inputString = \"$inputString @@$paramName@@: @@$util.escapeJavaScript($allParams.header.get($paramName))@@\")\n #if($foreach.hasNext)\n #set($inputString = \"$inputString,\")\n #end\n #end\n #set($inputString = \"$inputString }\")\n \n #end\n\n #if ($includeQueryString)\n #set($inputString = \"$inputString, @@querystring@@:{\")\n #foreach($paramName in $allParams.querystring.keySet())\n #set($inputString = \"$inputString @@$paramName@@: @@$util.escapeJavaScript($allParams.querystring.get($paramName))@@\")\n #if($foreach.hasNext)\n #set($inputString = \"$inputString,\")\n #end\n #end\n #set($inputString = \"$inputString }\")\n #end\n\n #if ($includePath)\n #set($inputString = \"$inputString, @@path@@:{\")\n #foreach($paramName in $allParams.path.keySet())\n #set($inputString = \"$inputString @@$paramName@@: @@$util.escapeJavaScript($allParams.path.get($paramName))@@\")\n #if($foreach.hasNext)\n #set($inputString = \"$inputString,\")\n #end\n #end\n #set($inputString = \"$inputString }\")\n #end\n \n #if ($includeAuthorizer)\n #set($inputString = \"$inputString, @@authorizer@@:{\")\n #foreach($paramName in $context.authorizer.keySet())\n #set($inputString = \"$inputString @@$paramName@@: @@$util.escapeJavaScript($context.authorizer.get($paramName))@@\")\n #if($foreach.hasNext)\n #set($inputString = \"$inputString,\")\n #end\n #end\n #set($inputString = \"$inputString }\")\n #end\n\n #set($requestContext = \"\")\n ## Check if the request context should be included as part of the execution input\n #if($requestContext && !$requestContext.empty)\n #set($inputString = \"$inputString,\")\n #set($inputString = \"$inputString @@requestContext@@: $requestContext\")\n #end\n\n #set($inputString = \"$inputString}\")\n #set($inputString = $inputString.replaceAll(\"@@\",'\"'))\n #set($len = $inputString.length() - 1)\n \"input\": \"{$util.escapeJavaScript($inputString.substring(1,$len)).replaceAll(\"\\\\'\",\"'\")}\"\n}\n", ], ], }, From 8585ba69cf25a4555a565728a639561224b5e9da Mon Sep 17 00:00:00 2001 From: Kyle Laker Date: Wed, 13 Jul 2022 23:18:56 -0400 Subject: [PATCH 43/92] docs(logs): clarify encryption default for log groups (#21060) The default encryption for CloudWatch Logs is not performed via a KMS AWS-managed key which is what is usually implied by "master key". Updating the documentation to clarify that this is server-side encryption hopefully clarifies why "KMS key ID" shows as "-" in the Console for created Log Groups and why the `cloudwatch-log-group-encrypted` Config Rule shows as non-compliant. Based on https://docs.aws.amazon.com/AmazonCloudWatch/latest/logs/data-protection.html#encryption-rest ---- ### All Submissions: * [X] Have you followed the guidelines in our [Contributing guide?](https://github.com/aws/aws-cdk/blob/main/CONTRIBUTING.md) ### Adding new Unconventional Dependencies: * [ ] This PR adds new unconventional dependencies following the process described [here](https://github.com/aws/aws-cdk/blob/main/CONTRIBUTING.md/#adding-new-unconventional-dependencies) ### New Features * [ ] Have you added the new feature to an [integration test](https://github.com/aws/aws-cdk/blob/main/INTEGRATION_TESTS.md)? * [ ] Did you use `yarn integ` to deploy the infrastructure and generate the snapshot (i.e. `yarn integ` without `--dry-run`)? *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- packages/@aws-cdk/aws-logs/lib/log-group.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/@aws-cdk/aws-logs/lib/log-group.ts b/packages/@aws-cdk/aws-logs/lib/log-group.ts index 293762305e537..3776b0c28f83a 100644 --- a/packages/@aws-cdk/aws-logs/lib/log-group.ts +++ b/packages/@aws-cdk/aws-logs/lib/log-group.ts @@ -349,9 +349,9 @@ export enum RetentionDays { */ export interface LogGroupProps { /** - * The KMS Key to encrypt the log group with. + * The KMS customer managed key to encrypt the log group with. * - * @default - log group is encrypted with the default master key + * @default Server-side encrpytion managed by the CloudWatch Logs service */ readonly encryptionKey?: kms.IKey; From e1230877d86fcec9bc3e18c0afce860dd08b33c2 Mon Sep 17 00:00:00 2001 From: WinterYukky <49480575+WinterYukky@users.noreply.github.com> Date: Thu, 14 Jul 2022 12:58:35 +0900 Subject: [PATCH 44/92] fix(core): entrypoint option never used (#21124) ## Summary `asset-staging.ts` does not use the entrypoint option received as `BundlingOptions`. This PR is fix it. ## Why need this change Currently, `asset-staging.ts` receives the entrypoint option from the user, but it is not passed to `DockerImage.run` and does not execute the Docker build command expected by the user. This is maybe bug. ---- ### All Submissions: * [x] Have you followed the guidelines in our [Contributing guide?](https://github.com/aws/aws-cdk/blob/main/CONTRIBUTING.md) ### Adding new Unconventional Dependencies: * [ ] This PR adds new unconventional dependencies following the process described [here](https://github.com/aws/aws-cdk/blob/main/CONTRIBUTING.md/#adding-new-unconventional-dependencies) ### New Features * [ ] Have you added the new feature to an [integration test](https://github.com/aws/aws-cdk/blob/main/INTEGRATION_TESTS.md)? * [ ] Did you use `yarn integ` to deploy the infrastructure and generate the snapshot (i.e. `yarn integ` without `--dry-run`)? *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- packages/@aws-cdk/core/lib/asset-staging.ts | 1 + packages/@aws-cdk/core/test/staging.test.ts | 25 +++++++++++++++++++++ 2 files changed, 26 insertions(+) diff --git a/packages/@aws-cdk/core/lib/asset-staging.ts b/packages/@aws-cdk/core/lib/asset-staging.ts index b6a84d9af0961..7ad4992f29773 100644 --- a/packages/@aws-cdk/core/lib/asset-staging.ts +++ b/packages/@aws-cdk/core/lib/asset-staging.ts @@ -456,6 +456,7 @@ export class AssetStaging extends Construct { user, volumes, environment: options.environment, + entrypoint: options.entrypoint, workingDirectory: options.workingDirectory ?? AssetStaging.BUNDLING_INPUT_DIR, securityOpt: options.securityOpt ?? '', }); diff --git a/packages/@aws-cdk/core/test/staging.test.ts b/packages/@aws-cdk/core/test/staging.test.ts index a1cf81aec2eed..aa8e57814c98a 100644 --- a/packages/@aws-cdk/core/test/staging.test.ts +++ b/packages/@aws-cdk/core/test/staging.test.ts @@ -612,6 +612,31 @@ describe('staging', () => { expect(asset.assetHash).toEqual('33cbf2cae5432438e0f046bc45ba8c3cef7b6afcf47b59d1c183775c1918fb1f'); }); + test('bundling with docker entrypoint', () => { + // GIVEN + const app = new App(); + const stack = new Stack(app, 'stack'); + const directory = path.join(__dirname, 'fs', 'fixtures', 'test1'); + + // WHEN + const asset = new AssetStaging(stack, 'Asset', { + sourcePath: directory, + bundling: { + image: DockerImage.fromRegistry('alpine'), + entrypoint: [DockerStubCommand.SUCCESS], + command: [DockerStubCommand.SUCCESS], + }, + assetHashType: AssetHashType.OUTPUT, + }); + + // THEN + expect( + readDockerStubInput()).toEqual( + `run --rm ${USER_ARG} -v /input:/asset-input:delegated -v /output:/asset-output:delegated -w /asset-input --entrypoint DOCKER_STUB_SUCCESS alpine DOCKER_STUB_SUCCESS`, + ); + expect(asset.assetHash).toEqual('33cbf2cae5432438e0f046bc45ba8c3cef7b6afcf47b59d1c183775c1918fb1f'); + }); + test('bundling with OUTPUT asset hash type', () => { // GIVEN const app = new App(); From 8446c5ceb7e400966e573bccaea40378541b0579 Mon Sep 17 00:00:00 2001 From: soucema9 <57732342+soucema9@users.noreply.github.com> Date: Thu, 14 Jul 2022 06:33:37 +0200 Subject: [PATCH 45/92] fix(events-targets): api destination target ignores pathParameterValues and queryStringParameters (#21111) fixes #21101 ---- ### All Submissions: * [x] Have you followed the guidelines in our [Contributing guide?](https://github.com/aws/aws-cdk/blob/main/CONTRIBUTING.md) ### Adding new Unconventional Dependencies: * [ ] This PR adds new unconventional dependencies following the process described [here](https://github.com/aws/aws-cdk/blob/main/CONTRIBUTING.md/#adding-new-unconventional-dependencies) ### New Features * [ ] Have you added the new feature to an [integration test](https://github.com/aws/aws-cdk/blob/main/INTEGRATION_TESTS.md)? * [ ] Did you use `yarn integ` to deploy the infrastructure and generate the snapshot (i.e. `yarn integ` without `--dry-run`)? *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- .../aws-events-targets/lib/api-destination.ts | 6 +- .../api-destination/api-destination.test.ts | 60 +++++++++++++++++++ 2 files changed, 63 insertions(+), 3 deletions(-) diff --git a/packages/@aws-cdk/aws-events-targets/lib/api-destination.ts b/packages/@aws-cdk/aws-events-targets/lib/api-destination.ts index 5ec83563ade94..675077f44dcf6 100644 --- a/packages/@aws-cdk/aws-events-targets/lib/api-destination.ts +++ b/packages/@aws-cdk/aws-events-targets/lib/api-destination.ts @@ -70,9 +70,9 @@ export class ApiDestination implements events.IRuleTarget { */ public bind(_rule: events.IRule, _id?: string): events.RuleTargetConfig { const httpParameters: events.CfnRule.HttpParametersProperty | undefined = - !!this.props.headerParameters ?? - !!this.props.pathParameterValues ?? - !!this.props.queryStringParameters + this.props.headerParameters ?? + this.props.pathParameterValues ?? + this.props.queryStringParameters ? { headerParameters: this.props.headerParameters, pathParameterValues: this.props.pathParameterValues, diff --git a/packages/@aws-cdk/aws-events-targets/test/api-destination/api-destination.test.ts b/packages/@aws-cdk/aws-events-targets/test/api-destination/api-destination.test.ts index 93ad80a233272..085742ef86d21 100644 --- a/packages/@aws-cdk/aws-events-targets/test/api-destination/api-destination.test.ts +++ b/packages/@aws-cdk/aws-events-targets/test/api-destination/api-destination.test.ts @@ -66,4 +66,64 @@ describe('with basic auth connection', () => { ], }); }); + + test('with header parameter', () => { + // WHEN + rule.addTarget(new targets.ApiDestination(destination, { + headerParameters: { headerName: 'headerValue' }, + })); + + // THEN + const template = Template.fromStack(stack); + template.hasResourceProperties('AWS::Events::Rule', { + Targets: [ + { + Arn: { 'Fn::GetAtt': ['DestinationApiDestinationA879FAE5', 'Arn'] }, + Id: 'Target0', + RoleArn: { 'Fn::GetAtt': ['DestinationEventsRole7DA63556', 'Arn'] }, + HttpParameters: { HeaderParameters: { headerName: 'headerValue' } }, + }, + ], + }); + }); + + test('with query parameter', () => { + // WHEN + rule.addTarget(new targets.ApiDestination(destination, { + queryStringParameters: { queryName: 'queryValue' }, + })); + + // THEN + const template = Template.fromStack(stack); + template.hasResourceProperties('AWS::Events::Rule', { + Targets: [ + { + Arn: { 'Fn::GetAtt': ['DestinationApiDestinationA879FAE5', 'Arn'] }, + Id: 'Target0', + RoleArn: { 'Fn::GetAtt': ['DestinationEventsRole7DA63556', 'Arn'] }, + HttpParameters: { QueryStringParameters: { queryName: 'queryValue' } }, + }, + ], + }); + }); + + test('with path parameter', () => { + // WHEN + rule.addTarget(new targets.ApiDestination(destination, { + pathParameterValues: ['pathValue'], + })); + + // THEN + const template = Template.fromStack(stack); + template.hasResourceProperties('AWS::Events::Rule', { + Targets: [ + { + Arn: { 'Fn::GetAtt': ['DestinationApiDestinationA879FAE5', 'Arn'] }, + Id: 'Target0', + RoleArn: { 'Fn::GetAtt': ['DestinationEventsRole7DA63556', 'Arn'] }, + HttpParameters: { PathParameterValues: ['pathValue'] }, + }, + ], + }); + }); }); From 53a6a479bf7cd5ae58b29762cde8b371c03f7864 Mon Sep 17 00:00:00 2001 From: Julian Michel Date: Thu, 14 Jul 2022 07:43:46 +0200 Subject: [PATCH 46/92] feat(backup): support RDS database cluster and serverless cluster (#17971) Add methods `fromRdsDatabaseCluster` and `fromRdsServerlessCluster` to support RDS database cluster and serverless cluster in AWS Backup. In `BackupableResourcesCollector`, `CfnDBCluster` will be detected and added to the backup selection. Missing unit tests for `fromRdsDatabaseInstance` were added as well. Change behaviour of `CfnDBInstance` in `BackupableResourcesCollector`. Database instances will be added only if attribute `dbClusterIdentifier` is not set. In the current implementation, database instances of a database cluster will be added to the backup selection. In my point of view, it is not necessary to backup each database instance of a database cluster if the database cluster is selected. Please check if this change is correct and allowed. Closes #16457. ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- packages/@aws-cdk/aws-backup/README.md | 20 ++ .../lib/backupable-resources-collector.ts | 14 +- packages/@aws-cdk/aws-backup/lib/resource.ts | 15 + .../aws-backup/rosetta/default.ts-fixture | 2 + .../aws-backup/test/selection.test.ts | 335 ++++++++++++++++++ 5 files changed, 385 insertions(+), 1 deletion(-) diff --git a/packages/@aws-cdk/aws-backup/README.md b/packages/@aws-cdk/aws-backup/README.md index 687d0b9d800bd..d15bc5d6b4e30 100644 --- a/packages/@aws-cdk/aws-backup/README.md +++ b/packages/@aws-cdk/aws-backup/README.md @@ -34,12 +34,32 @@ Assigning resources to a plan can be done with `addSelection()`: ```ts declare const plan: backup.BackupPlan; +declare const vpc: ec2.Vpc; const myTable = dynamodb.Table.fromTableName(this, 'Table', 'myTableName'); +const myDatabaseInstance = new rds.DatabaseInstance(this, 'DatabaseInstance', { + engine: rds.DatabaseInstanceEngine.mysql({ version: rds.MysqlEngineVersion.VER_8_0_26 }), + vpc, +}); +const myDatabaseCluster = new rds.DatabaseCluster(this, 'DatabaseCluster', { + engine: rds.DatabaseClusterEngine.auroraMysql({ version: rds.AuroraMysqlEngineVersion.VER_2_08_1 }), + credentials: rds.Credentials.fromGeneratedSecret('clusteradmin'), + instanceProps: { + vpc, + }, +}); +const myServerlessCluster = new rds.ServerlessCluster(this, 'ServerlessCluster', { + engine: rds.DatabaseClusterEngine.AURORA_POSTGRESQL, + parameterGroup: rds.ParameterGroup.fromParameterGroupName(this, 'ParameterGroup', 'default.aurora-postgresql10'), + vpc, +}); const myCoolConstruct = new Construct(this, 'MyCoolConstruct'); plan.addSelection('Selection', { resources: [ backup.BackupResource.fromDynamoDbTable(myTable), // A DynamoDB table + backup.BackupResource.fromRdsDatabaseInstance(myDatabaseInstance), // A RDS instance + backup.BackupResource.fromRdsDatabaseCluster(myDatabaseCluster), // A RDS database cluster + backup.BackupResource.fromRdsServerlessCluster(myServerlessCluster), // An Aurora Serverless cluster backup.BackupResource.fromTag('stage', 'prod'), // All resources that are tagged stage=prod in the region/account backup.BackupResource.fromConstruct(myCoolConstruct), // All backupable resources in `myCoolConstruct` ] diff --git a/packages/@aws-cdk/aws-backup/lib/backupable-resources-collector.ts b/packages/@aws-cdk/aws-backup/lib/backupable-resources-collector.ts index f48702132f090..7dc5bbdf5efdb 100644 --- a/packages/@aws-cdk/aws-backup/lib/backupable-resources-collector.ts +++ b/packages/@aws-cdk/aws-backup/lib/backupable-resources-collector.ts @@ -42,9 +42,21 @@ export class BackupableResourcesCollector implements IAspect { } if (node instanceof rds.CfnDBInstance) { + const dbInstance = node as rds.CfnDBInstance; + if (!dbInstance.dbClusterIdentifier) { + this.resources.push(Stack.of(node).formatArn({ + service: 'rds', + resource: 'db', + arnFormat: ArnFormat.COLON_RESOURCE_NAME, + resourceName: node.ref, + })); + } + } + + if (node instanceof rds.CfnDBCluster) { this.resources.push(Stack.of(node).formatArn({ service: 'rds', - resource: 'db', + resource: 'cluster', arnFormat: ArnFormat.COLON_RESOURCE_NAME, resourceName: node.ref, })); diff --git a/packages/@aws-cdk/aws-backup/lib/resource.ts b/packages/@aws-cdk/aws-backup/lib/resource.ts index 07ac3f2103ec0..8afd52a58fdd8 100644 --- a/packages/@aws-cdk/aws-backup/lib/resource.ts +++ b/packages/@aws-cdk/aws-backup/lib/resource.ts @@ -98,6 +98,21 @@ export class BackupResource { return BackupResource.fromArn(instance.instanceArn); } + /** + * A RDS database cluter + */ + public static fromRdsDatabaseCluster(cluster: rds.IDatabaseCluster) { + const stack = Stack.of(cluster); + return BackupResource.fromArn(`arn:${stack.partition}:rds:${stack.region}:${stack.account}:cluster:${cluster.clusterIdentifier}`); + } + + /** + * An Aurora database instance + */ + public static fromRdsServerlessCluster(cluster: rds.IServerlessCluster) { + return BackupResource.fromArn(cluster.clusterArn); + } + /** * A list of ARNs or match patterns such as * `arn:aws:ec2:us-east-1:123456789012:volume/*` diff --git a/packages/@aws-cdk/aws-backup/rosetta/default.ts-fixture b/packages/@aws-cdk/aws-backup/rosetta/default.ts-fixture index 5f28d8bba18e2..1384bd0540ee8 100644 --- a/packages/@aws-cdk/aws-backup/rosetta/default.ts-fixture +++ b/packages/@aws-cdk/aws-backup/rosetta/default.ts-fixture @@ -7,6 +7,8 @@ import * as dynamodb from '@aws-cdk/aws-dynamodb'; import * as events from '@aws-cdk/aws-events'; import * as kms from '@aws-cdk/aws-kms'; import * as sns from '@aws-cdk/aws-sns'; +import * as rds from '@aws-cdk/aws-rds'; +import * as ec2 from '@aws-cdk/aws-ec2'; class Fixture extends Stack { constructor(scope: Construct, id: string) { diff --git a/packages/@aws-cdk/aws-backup/test/selection.test.ts b/packages/@aws-cdk/aws-backup/test/selection.test.ts index d25cd9bb41a18..3691d9b4adbd2 100644 --- a/packages/@aws-cdk/aws-backup/test/selection.test.ts +++ b/packages/@aws-cdk/aws-backup/test/selection.test.ts @@ -2,6 +2,7 @@ import { Template } from '@aws-cdk/assertions'; import * as dynamodb from '@aws-cdk/aws-dynamodb'; import * as ec2 from '@aws-cdk/aws-ec2'; import * as efs from '@aws-cdk/aws-efs'; +import * as rds from '@aws-cdk/aws-rds'; import { RemovalPolicy, Stack } from '@aws-cdk/core'; import { Construct } from 'constructs'; import { BackupPlan, BackupResource, BackupSelection } from '../lib'; @@ -140,6 +141,28 @@ test('fromConstruct', () => { }); new EfsConstruct(this, 'EFS'); + + const vpc = new ec2.Vpc(this, 'Vpc'); + + new rds.DatabaseInstance(this, 'DatabaseInstance', { + engine: rds.DatabaseInstanceEngine.mysql({ version: rds.MysqlEngineVersion.VER_8_0_26 }), + instanceType: ec2.InstanceType.of(ec2.InstanceClass.BURSTABLE3, ec2.InstanceSize.SMALL), + vpc, + }); + + new rds.DatabaseCluster(this, 'DatabaseCluster', { + engine: rds.DatabaseClusterEngine.auroraMysql({ version: rds.AuroraMysqlEngineVersion.VER_2_08_1 }), + credentials: rds.Credentials.fromGeneratedSecret('clusteradmin'), + instanceProps: { + vpc, + }, + }); + + new rds.ServerlessCluster(this, 'ServerlessCluster', { + engine: rds.DatabaseClusterEngine.AURORA_POSTGRESQL, + parameterGroup: rds.ParameterGroup.fromParameterGroupName(stack, 'ParameterGroup', 'default.aurora-postgresql10'), + vpc, + }); } } const myConstruct = new MyConstruct(stack, 'MyConstruct'); @@ -209,6 +232,76 @@ test('fromConstruct', () => { ], ], }, + { + 'Fn::Join': [ + '', + [ + 'arn:', + { + Ref: 'AWS::Partition', + }, + ':rds:', + { + Ref: 'AWS::Region', + }, + ':', + { + Ref: 'AWS::AccountId', + }, + ':db:', + { + Ref: 'MyConstructDatabaseInstanceC3164567', + }, + ], + ], + }, + { + 'Fn::Join': [ + '', + [ + 'arn:', + { + Ref: 'AWS::Partition', + }, + ':rds:', + { + Ref: 'AWS::Region', + }, + ':', + { + Ref: 'AWS::AccountId', + }, + ':cluster:', + { + Ref: 'MyConstructDatabaseCluster9BAC1530', + }, + ], + ], + }, + { + 'Fn::Join': [ + '', + [ + 'arn:', + { + Ref: 'AWS::Partition', + }, + ':rds:', + { + Ref: 'AWS::Region', + }, + ':', + { + Ref: 'AWS::AccountId', + }, + ':cluster:', + { + Ref: 'MyConstructServerlessCluster90C61A45', + }, + ], + ], + }, + { 'Fn::Join': [ '', @@ -333,3 +426,245 @@ test('fromDynamoDbTable', () => { }, }); }); + +test('fromRdsDatabaseInstance', () => { + // GIVEN + const vpc = new ec2.Vpc(stack, 'Vpc'); + const newInstance = new rds.DatabaseInstance(stack, 'New', { + engine: rds.DatabaseInstanceEngine.mysql({ version: rds.MysqlEngineVersion.VER_8_0_26 }), + instanceType: ec2.InstanceType.of(ec2.InstanceClass.BURSTABLE3, ec2.InstanceSize.SMALL), + vpc, + }); + const existingInstance = rds.DatabaseInstance.fromDatabaseInstanceAttributes(stack, 'Existing', { + instanceEndpointAddress: 'address', + instanceIdentifier: 'existing-instance', + port: 3306, + securityGroups: [], + }); + + // WHEN + plan.addSelection('Selection', { + resources: [ + BackupResource.fromRdsDatabaseInstance(newInstance), + BackupResource.fromRdsDatabaseInstance(existingInstance), + ], + }); + + // THEN + Template.fromStack(stack).hasResourceProperties('AWS::Backup::BackupSelection', { + BackupSelection: { + IamRoleArn: { + 'Fn::GetAtt': [ + 'PlanSelectionRole6D10F4B7', + 'Arn', + ], + }, + Resources: [ + { + 'Fn::Join': [ + '', + [ + 'arn:', + { + Ref: 'AWS::Partition', + }, + ':rds:', + { + Ref: 'AWS::Region', + }, + ':', + { + Ref: 'AWS::AccountId', + }, + ':db:', + { + Ref: 'New8A81B073', + }, + ], + ], + }, + { + 'Fn::Join': [ + '', + [ + 'arn:', + { + Ref: 'AWS::Partition', + }, + ':rds:', + { + Ref: 'AWS::Region', + }, + ':', + { + Ref: 'AWS::AccountId', + }, + ':db:existing-instance', + ], + ], + }, + ], + SelectionName: 'Selection', + }, + }); +}); + +test('fromRdsDatabaseCluster', () => { + // GIVEN + const vpc = new ec2.Vpc(stack, 'Vpc'); + const newCluster = new rds.DatabaseCluster(stack, 'New', { + engine: rds.DatabaseClusterEngine.auroraMysql({ version: rds.AuroraMysqlEngineVersion.VER_2_08_1 }), + credentials: rds.Credentials.fromGeneratedSecret('clusteradmin'), + instanceProps: { + vpc, + }, + }); + const existingCluster = rds.DatabaseCluster.fromDatabaseClusterAttributes(stack, 'Existing', { + clusterIdentifier: 'existing-cluster', + }); + + // WHEN + plan.addSelection('Selection', { + resources: [ + BackupResource.fromRdsDatabaseCluster(newCluster), + BackupResource.fromRdsDatabaseCluster(existingCluster), + ], + }); + + // THEN + Template.fromStack(stack).hasResourceProperties('AWS::Backup::BackupSelection', { + BackupSelection: { + IamRoleArn: { + 'Fn::GetAtt': [ + 'PlanSelectionRole6D10F4B7', + 'Arn', + ], + }, + Resources: [ + { + 'Fn::Join': [ + '', + [ + 'arn:', + { + Ref: 'AWS::Partition', + }, + ':rds:', + { + Ref: 'AWS::Region', + }, + ':', + { + Ref: 'AWS::AccountId', + }, + ':cluster:', + { + Ref: 'New8A81B073', + }, + ], + ], + }, + { + 'Fn::Join': [ + '', + [ + 'arn:', + { + Ref: 'AWS::Partition', + }, + ':rds:', + { + Ref: 'AWS::Region', + }, + ':', + { + Ref: 'AWS::AccountId', + }, + ':cluster:existing-cluster', + ], + ], + }, + ], + SelectionName: 'Selection', + }, + }); +}); + +test('fromRdsServerlessCluster', () => { + // GIVEN + const vpc = new ec2.Vpc(stack, 'Vpc'); + const newCluster = new rds.ServerlessCluster(stack, 'New', { + engine: rds.DatabaseClusterEngine.AURORA_POSTGRESQL, + parameterGroup: rds.ParameterGroup.fromParameterGroupName(stack, 'ParameterGroup', 'default.aurora-postgresql10'), + vpc, + }); + const existingCluster = rds.ServerlessCluster.fromServerlessClusterAttributes(stack, 'Existing', { + clusterIdentifier: 'existing-cluster', + }); + + // WHEN + plan.addSelection('Selection', { + resources: [ + BackupResource.fromRdsServerlessCluster(newCluster), + BackupResource.fromRdsServerlessCluster(existingCluster), + ], + }); + + // THEN + Template.fromStack(stack).hasResourceProperties('AWS::Backup::BackupSelection', { + BackupSelection: { + IamRoleArn: { + 'Fn::GetAtt': [ + 'PlanSelectionRole6D10F4B7', + 'Arn', + ], + }, + Resources: [ + { + 'Fn::Join': [ + '', + [ + 'arn:', + { + Ref: 'AWS::Partition', + }, + ':rds:', + { + Ref: 'AWS::Region', + }, + ':', + { + Ref: 'AWS::AccountId', + }, + ':cluster:', + { + Ref: 'New8A81B073', + }, + ], + ], + }, + { + 'Fn::Join': [ + '', + [ + 'arn:', + { + Ref: 'AWS::Partition', + }, + ':rds:', + { + Ref: 'AWS::Region', + }, + ':', + { + Ref: 'AWS::AccountId', + }, + ':cluster:existing-cluster', + ], + ], + }, + ], + SelectionName: 'Selection', + }, + }); +}); From 47ee16cb56f550d055d284185e65498069543d5b Mon Sep 17 00:00:00 2001 From: Eli Polonsky Date: Thu, 14 Jul 2022 09:19:48 +0300 Subject: [PATCH 47/92] docs(cli): bundling implications on contributions (#19449) Added a good to know section about CLI bundling to the contribution guide. ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- packages/aws-cdk/CONTRIBUTING.md | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/packages/aws-cdk/CONTRIBUTING.md b/packages/aws-cdk/CONTRIBUTING.md index c5afd84242be0..1df34c8fe7985 100644 --- a/packages/aws-cdk/CONTRIBUTING.md +++ b/packages/aws-cdk/CONTRIBUTING.md @@ -201,3 +201,32 @@ These two sets of integration tests have 3 running modes: The integration test and canary modes are used in the CDK publishing pipeline and the CDK canaries, respectively. You wouldn't normally need to run them directly that way. + +## Bundling + +Our CLI package is built and packaged using the [node-bundle](../../tools/%40aws-cdk/node-bundle/README.md) tool. + +This has two affects one should be aware of: + +### Runtime Dependencies + +All runtime dependencies are converted to `devDependencies`, as they are bundled inside +the package and don't require installation by consumers. This process happens on-demand during packaging, +this is why our [source code](./package.json) still contains those dependencies, +but the [npm](https://www.npmjs.com/package/aws-cdk) package does not. + +### Attributions + +The bundler also creates an attributions document that lists out license information for the entire +dependency closure. This document is stored in the [THIRD_PARTY_LICENSES](./THIRD_PARTY_LICENSES) file. +Our build process validates that the file committed to source matches the expected auto-generated one. +We do this so that our source code always contains the up to date attributions document, and so that we can +backtrack/review changes to it using normal code review processes. + +Whenever a dependency changes (be it direct or transitive, new package or new version), the attributions document +will change, and needs to be regenerated. For you, this means that: + +1. When you manually upgrade a dependency, you must also regenerate the document by running `yarn pkglint` inside the CLI package. +2. When you build the CLI locally, you must ensure your dependencies are up to date by running `yarn install` inside the CLI package. +Otherwise, you might get an error like so: `aws-cdk: - [bundle/outdated-attributions] THIRD_PARTY_LICENSES is outdated (fixable)`. + From ab7cff6766d432f47bac861b2deadf15c906fad0 Mon Sep 17 00:00:00 2001 From: Joshua Weber <57131123+daschaa@users.noreply.github.com> Date: Thu, 14 Jul 2022 08:54:54 +0200 Subject: [PATCH 48/92] docs(contributing): add additional step in the feature flags section (#21074) [Follow-Up to this Comment.](https://github.com/aws/aws-cdk/pull/20521#discussion_r916749609) > Can you also add this info [here](https://github.com/aws/aws-cdk/blob/68f09b7c6f8e6c1ddf13fdd4e116d12333d24c46/packages/%40aws-cdk/cx-api/README.md?plain=1#L14)? We haven't found the best way to document these yet so until then I figure that will have to do. In the contributing documentation for the feature flags the part for adding an entry in the feature flags README was added in this PR. ---- ### All Submissions: * [x] Have you followed the guidelines in our [Contributing guide?](https://github.com/aws/aws-cdk/blob/main/CONTRIBUTING.md) ### Adding new Unconventional Dependencies: * [ ] This PR adds new unconventional dependencies following the process described [here](https://github.com/aws/aws-cdk/blob/main/CONTRIBUTING.md/#adding-new-unconventional-dependencies) ### New Features * [ ] Have you added the new feature to an [integration test](https://github.com/aws/aws-cdk/blob/main/INTEGRATION_TESTS.md)? * [ ] Did you use `yarn integ` to deploy the infrastructure and generate the snapshot (i.e. `yarn integ` without `--dry-run`)? *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- CONTRIBUTING.md | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 2ffc461c12da5..0c36af6d541d6 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -958,8 +958,16 @@ The pattern is simple: [cx-api/lib/features.ts](https://github.com/aws/aws-cdk/blob/main/packages/%40aws-cdk/cx-api/lib/features.ts). This map is inserted to generated `cdk.json` files for new projects created through `cdk init`. -4. In your tests, use the `testFutureBehavior` and `testLegacyBehavior` [jest helper methods] to test the enabled and disabled behavior. -5. In your PR title (which goes into CHANGELOG), add a `(under feature flag)` suffix. e.g: +4. Add an entry for your feature flag in the [README](https://github.com/aws/aws-cdk/blob/main/packages/%40aws-cdk/cx-api/README.md) file. +5. In your tests, ensure that you test your feature with and without the feature flag enabled. You can do this by passing the feature flag to the `context` property when instantiating an `App`. + ```ts + const myFeatureFlag = { [cxapi.MY_FEATURE_FLAG]: true }; + const app = new App({ + context: myFeatureFlag, + }), + const stackUnderTest = new Stack(app); + ``` +7. In your PR title (which goes into CHANGELOG), add a `(under feature flag)` suffix. e.g: `fix(core): impossible to use the same physical stack name for two stacks (under feature flag)` From 424b1577895c6b44a1dca82a722c9c5adc3f66e7 Mon Sep 17 00:00:00 2001 From: Joshua Weber <57131123+daschaa@users.noreply.github.com> Date: Thu, 14 Jul 2022 09:30:30 +0200 Subject: [PATCH 49/92] docs(s3): add example to s3 bucket documentation (#21110) Fixes #20374 Adds an example to the S3 `Bucket` docstring. ---- ### All Submissions: * [x] Have you followed the guidelines in our [Contributing guide?](https://github.com/aws/aws-cdk/blob/main/CONTRIBUTING.md) ### Adding new Unconventional Dependencies: * [ ] This PR adds new unconventional dependencies following the process described [here](https://github.com/aws/aws-cdk/blob/main/CONTRIBUTING.md/#adding-new-unconventional-dependencies) ### New Features * [ ] Have you added the new feature to an [integration test](https://github.com/aws/aws-cdk/blob/main/INTEGRATION_TESTS.md)? * [ ] Did you use `yarn integ` to deploy the infrastructure and generate the snapshot (i.e. `yarn integ` without `--dry-run`)? *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- packages/@aws-cdk/aws-s3/lib/bucket.ts | 27 ++++++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/packages/@aws-cdk/aws-s3/lib/bucket.ts b/packages/@aws-cdk/aws-s3/lib/bucket.ts index 84d2beecfb5c1..dba8bffb850d0 100644 --- a/packages/@aws-cdk/aws-s3/lib/bucket.ts +++ b/packages/@aws-cdk/aws-s3/lib/bucket.ts @@ -4,8 +4,20 @@ import * as events from '@aws-cdk/aws-events'; import * as iam from '@aws-cdk/aws-iam'; import * as kms from '@aws-cdk/aws-kms'; import { - Fn, IResource, Lazy, RemovalPolicy, Resource, ResourceProps, Stack, Token, - CustomResource, CustomResourceProvider, CustomResourceProviderRuntime, FeatureFlags, Tags, Duration, + CustomResource, + CustomResourceProvider, + CustomResourceProviderRuntime, + Duration, + FeatureFlags, + Fn, + IResource, + Lazy, + RemovalPolicy, + Resource, + ResourceProps, + Stack, + Tags, + Token, } from '@aws-cdk/core'; import * as cxapi from '@aws-cdk/cx-api'; import { Construct } from 'constructs'; @@ -1550,6 +1562,17 @@ export interface Tag { * * This bucket does not yet have all features that exposed by the underlying * BucketResource. + * + * @example + * + * new Bucket(scope, 'Bucket', { + * blockPublicAccess: BlockPublicAccess.BLOCK_ALL, + * encryption: BucketEncryption.S3_MANAGED, + * enforceSSL: true, + * versioned: true, + * removalPolicy: RemovalPolicy.RETAIN, + * }); + * */ export class Bucket extends BucketBase { From 0b5123ada8c7dfdfe6f55ec8882b6fccc3a5168d Mon Sep 17 00:00:00 2001 From: Eli Polonsky Date: Thu, 14 Jul 2022 17:45:58 +0300 Subject: [PATCH 50/92] fix(ec2): deprecated `SubnetType` enums are treated incorrectly (#21140) In https://github.com/aws/aws-cdk/pull/19320, we changed the values of deprecated enums to include a `Deprecated_` prefix. This also meant we had to do some gymnastics in the code to change usages of the value to a switch case. However, there were also places in the code that do enum comparison, which uses the enum value. Those comparison points need to also explicitly consider the equivalent deprecated enums. Fixes https://github.com/aws/aws-cdk/issues/21131, Fixes https://github.com/aws/aws-cdk/issues/21138 ---- ### All Submissions: * [ ] Have you followed the guidelines in our [Contributing guide?](https://github.com/aws/aws-cdk/blob/main/CONTRIBUTING.md) ### Adding new Unconventional Dependencies: * [ ] This PR adds new unconventional dependencies following the process described [here](https://github.com/aws/aws-cdk/blob/main/CONTRIBUTING.md/#adding-new-unconventional-dependencies) ### New Features * [ ] Have you added the new feature to an [integration test](https://github.com/aws/aws-cdk/blob/main/INTEGRATION_TESTS.md)? * [ ] Did you use `yarn integ` to deploy the infrastructure and generate the snapshot (i.e. `yarn integ` without `--dry-run`)? *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- packages/@aws-cdk/aws-ec2/lib/vpc.ts | 5 +- packages/@aws-cdk/aws-ec2/test/vpc.test.ts | 67 ++++++++++++++++++++++ 2 files changed, 70 insertions(+), 2 deletions(-) diff --git a/packages/@aws-cdk/aws-ec2/lib/vpc.ts b/packages/@aws-cdk/aws-ec2/lib/vpc.ts index 90021b3d501e5..f764b81425b9d 100644 --- a/packages/@aws-cdk/aws-ec2/lib/vpc.ts +++ b/packages/@aws-cdk/aws-ec2/lib/vpc.ts @@ -1368,7 +1368,7 @@ export class Vpc extends VpcBase { this.createSubnets(); const allowOutbound = this.subnetConfiguration.filter( - subnet => (subnet.subnetType !== SubnetType.PRIVATE_ISOLATED)).length > 0; + subnet => (subnet.subnetType !== SubnetType.PRIVATE_ISOLATED && subnet.subnetType !== SubnetType.ISOLATED)).length > 0; // Create an Internet Gateway and attach it if necessary if (allowOutbound) { @@ -2205,7 +2205,8 @@ class ImportedSubnet extends Resource implements ISubnet, IPublicSubnet, IPrivat * They seem pointless but I see no reason to prevent it. */ function determineNatGatewayCount(requestedCount: number | undefined, subnetConfig: SubnetConfiguration[], azCount: number) { - const hasPrivateSubnets = subnetConfig.some(c => c.subnetType === SubnetType.PRIVATE_WITH_NAT && !c.reserved); + const hasPrivateSubnets = subnetConfig.some(c => (c.subnetType === SubnetType.PRIVATE_WITH_NAT + || c.subnetType === SubnetType.PRIVATE) && !c.reserved); const hasPublicSubnets = subnetConfig.some(c => c.subnetType === SubnetType.PUBLIC); const count = requestedCount !== undefined ? Math.min(requestedCount, azCount) : (hasPrivateSubnets ? azCount : 0); diff --git a/packages/@aws-cdk/aws-ec2/test/vpc.test.ts b/packages/@aws-cdk/aws-ec2/test/vpc.test.ts index 0000bdb63cee1..27d3875f5fde6 100644 --- a/packages/@aws-cdk/aws-ec2/test/vpc.test.ts +++ b/packages/@aws-cdk/aws-ec2/test/vpc.test.ts @@ -30,6 +30,73 @@ import { describe('vpc', () => { describe('When creating a VPC', () => { + + test('SubnetType.PRIVATE is equivalent to SubnetType.PRIVATE_WITH_NAT', () => { + + const stack1 = getTestStack(); + const stack2 = getTestStack(); + new Vpc(stack1, 'TheVPC', { + subnetConfiguration: [ + { + subnetType: SubnetType.PRIVATE, + name: 'subnet', + }, + { + subnetType: SubnetType.PUBLIC, + name: 'public', + }, + ], + }); + + new Vpc(stack2, 'TheVPC', { + subnetConfiguration: [ + { + subnetType: SubnetType.PRIVATE_WITH_NAT, + name: 'subnet', + }, + { + subnetType: SubnetType.PUBLIC, + name: 'public', + }, + ], + }); + + const t1 = Template.fromStack(stack1); + const t2 = Template.fromStack(stack2); + + expect(t1.toJSON()).toEqual(t2.toJSON()); + + }); + + test('SubnetType.ISOLATED is equivalent to SubnetType.PRIVATE_ISOLATED', () => { + + const stack1 = getTestStack(); + const stack2 = getTestStack(); + new Vpc(stack1, 'TheVPC', { + subnetConfiguration: [ + { + subnetType: SubnetType.ISOLATED, + name: 'subnet', + }, + ], + }); + + new Vpc(stack2, 'TheVPC', { + subnetConfiguration: [ + { + subnetType: SubnetType.PRIVATE_ISOLATED, + name: 'subnet', + }, + ], + }); + + const t1 = Template.fromStack(stack1); + const t2 = Template.fromStack(stack2); + + expect(t1.toJSON()).toEqual(t2.toJSON()); + + }); + describe('with the default CIDR range', () => { test('vpc.vpcId returns a token to the VPC ID', () => { From bd53ca4f83b8d34db804da2d4f035ff6f9867516 Mon Sep 17 00:00:00 2001 From: Pahud Hsieh Date: Thu, 14 Jul 2022 11:40:18 -0400 Subject: [PATCH 51/92] chore: fix the init task for gitpod (#21064) This PR fixes the init task in `.gitpod.yml` for gitpod to correctly prebuild and remove the eslint static version pinning. Fixes: #21063 ---- ### All Submissions: * [v] Have you followed the guidelines in our [Contributing guide?](https://github.com/aws/aws-cdk/blob/main/CONTRIBUTING.md) ### Adding new Unconventional Dependencies: * [ ] This PR adds new unconventional dependencies following the process described [here](https://github.com/aws/aws-cdk/blob/main/CONTRIBUTING.md/#adding-new-unconventional-dependencies) ### New Features * [ ] Have you added the new feature to an [integration test](https://github.com/aws/aws-cdk/blob/main/INTEGRATION_TESTS.md)? * [ ] Did you use `yarn integ` to deploy the infrastructure and generate the snapshot (i.e. `yarn integ` without `--dry-run`)? *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- .gitpod.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.gitpod.yml b/.gitpod.yml index ac367f2ce4329..ab9b430c9119c 100644 --- a/.gitpod.yml +++ b/.gitpod.yml @@ -6,8 +6,8 @@ github: image: jsii/superchain:1-buster-slim tasks: - - init: yarn build --skip-test --no-bail --skip-prereqs --skip-compat + - init: yarn install && ./scripts/foreach.sh yarn build vscode: extensions: - - dbaeumer.vscode-eslint@2.1.20 + - dbaeumer.vscode-eslint From 7b389f0e6ab123622677efb48c550ca6050d18bc Mon Sep 17 00:00:00 2001 From: Yoshihiro Ito Date: Fri, 15 Jul 2022 01:16:32 +0900 Subject: [PATCH 52/92] fix(core): updatedProperties function name is misspelled (#21071) This PR only fix typo in the name of protected function. `Properites` -> `Properties` ---- ### All Submissions: * [x] Have you followed the guidelines in our [Contributing guide?](https://github.com/aws/aws-cdk/blob/main/CONTRIBUTING.md) ### Adding new Unconventional Dependencies: * [ ] This PR adds new unconventional dependencies following the process described [here](https://github.com/aws/aws-cdk/blob/main/CONTRIBUTING.md/#adding-new-unconventional-dependencies) ### New Features * [ ] Have you added the new feature to an [integration test](https://github.com/aws/aws-cdk/blob/main/INTEGRATION_TESTS.md)? * [ ] Did you use `yarn integ` to deploy the infrastructure and generate the snapshot (i.e. `yarn integ` without `--dry-run`)? *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- packages/@aws-cdk/core/lib/cfn-resource.ts | 13 +++++++++++++ packages/@aws-cdk/core/test/resource.test.ts | 4 ++-- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/packages/@aws-cdk/core/lib/cfn-resource.ts b/packages/@aws-cdk/core/lib/cfn-resource.ts index a30ffda9ca3be..e1725c3b6789d 100644 --- a/packages/@aws-cdk/core/lib/cfn-resource.ts +++ b/packages/@aws-cdk/core/lib/cfn-resource.ts @@ -431,6 +431,9 @@ export class CfnResource extends CfnRefElement { } /** + * Deprecated + * @deprecated use `updatedProperties` + * * Return properties modified after initiation * * Resources that expose mutable properties should override this function to @@ -440,6 +443,16 @@ export class CfnResource extends CfnRefElement { return this._cfnProperties; } + /** + * Return properties modified after initiation + * + * Resources that expose mutable properties should override this function to + * collect and return the properties object for this resource. + */ + protected get updatedProperties(): { [key: string]: any } { + return this._cfnProperties; + } + protected validateProperties(_properties: any) { // Nothing } diff --git a/packages/@aws-cdk/core/test/resource.test.ts b/packages/@aws-cdk/core/test/resource.test.ts index d3dadcb056ff8..32a273f76a663 100644 --- a/packages/@aws-cdk/core/test/resource.test.ts +++ b/packages/@aws-cdk/core/test/resource.test.ts @@ -948,7 +948,7 @@ class CustomizableResource extends CfnResource { } public renderProperties(): { [key: string]: any } { - const props = this.updatedProperites; + const props = this.updatedProperties; const render: { [key: string]: any } = {}; for (const key of Object.keys(props)) { render[key.toUpperCase()] = props[key]; @@ -956,7 +956,7 @@ class CustomizableResource extends CfnResource { return render; } - protected get updatedProperites(): { [key: string]: any } { + protected get updatedProperties(): { [key: string]: any } { const props: { [key: string]: any } = { prop1: this.prop1, prop2: this.prop2, From 0cd83ccf6ec8d72f39cc4b8b066c8f4184174f90 Mon Sep 17 00:00:00 2001 From: Calvin Combs <66279577+comcalvi@users.noreply.github.com> Date: Thu, 14 Jul 2022 12:06:50 -0600 Subject: [PATCH 53/92] fix(kms): correctly recognize newly created resources (#21143) `principalIsANewlyCreatedResource()` was an imperfect solution to determining whether or not a principal already existed or if it was managed by CDK. This caused a deployment error, so this PR replaces it with `Resource.isOwnedResource()` which returns true iff a resource was created by CDK. This also updates the return type of `isResource()` to `is Resource`, because we already have a `is CfnResource` in `CfnResource`. Closes #19881. ---- ### All Submissions: * [x] Have you followed the guidelines in our [Contributing guide?](https://github.com/aws/aws-cdk/blob/main/CONTRIBUTING.md) ### Adding new Unconventional Dependencies: * [ ] This PR adds new unconventional dependencies following the process described [here](https://github.com/aws/aws-cdk/blob/main/CONTRIBUTING.md/#adding-new-unconventional-dependencies) ### New Features * [ ] Have you added the new feature to an [integration test](https://github.com/aws/aws-cdk/blob/main/INTEGRATION_TESTS.md)? * [ ] Did you use `yarn integ` to deploy the infrastructure and generate the snapshot (i.e. `yarn integ` without `--dry-run`)? *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- .../aws-iam/lib/private/immutable-role.ts | 1 + packages/@aws-cdk/aws-kms/lib/key.ts | 14 ++------ packages/@aws-cdk/aws-kms/test/key.test.ts | 35 +++++++++++++++++++ packages/@aws-cdk/core/lib/resource.ts | 9 ++++- 4 files changed, 47 insertions(+), 12 deletions(-) diff --git a/packages/@aws-cdk/aws-iam/lib/private/immutable-role.ts b/packages/@aws-cdk/aws-iam/lib/private/immutable-role.ts index 22c7374bf1771..8c7d5ca0c2646 100644 --- a/packages/@aws-cdk/aws-iam/lib/private/immutable-role.ts +++ b/packages/@aws-cdk/aws-iam/lib/private/immutable-role.ts @@ -39,6 +39,7 @@ export class ImmutableRole extends Resource implements IRole { Dependable.implement(this, { dependencyRoots: [role], }); + this.node.defaultChild = role.node.defaultChild; } public attachInlinePolicy(_policy: Policy): void { diff --git a/packages/@aws-cdk/aws-kms/lib/key.ts b/packages/@aws-cdk/aws-kms/lib/key.ts index 4c8993607e2f3..db334374593ed 100644 --- a/packages/@aws-cdk/aws-kms/lib/key.ts +++ b/packages/@aws-cdk/aws-kms/lib/key.ts @@ -2,7 +2,7 @@ import * as iam from '@aws-cdk/aws-iam'; import * as cxschema from '@aws-cdk/cloud-assembly-schema'; import { FeatureFlags, IResource, Lazy, RemovalPolicy, Resource, Stack, Duration, Token, ContextProvider, Arn, ArnFormat } from '@aws-cdk/core'; import * as cxapi from '@aws-cdk/cx-api'; -import { IConstruct, Construct } from 'constructs'; +import { Construct } from 'constructs'; import { Alias } from './alias'; import { KeyLookupOptions } from './key-lookup'; import { CfnKey } from './kms.generated'; @@ -208,28 +208,20 @@ abstract class KeyBase extends Resource implements IKey { } // this logic should only apply to newly created // (= not imported) resources - if (!this.principalIsANewlyCreatedResource(grantPrincipal)) { + if (!Resource.isOwnedResource(grantPrincipal)) { return undefined; } - // return undefined; const keyStack = Stack.of(this); const granteeStack = Stack.of(grantPrincipal); if (keyStack === granteeStack) { return undefined; } + return granteeStack.dependencies.includes(keyStack) ? granteeStack.account : undefined; } - private principalIsANewlyCreatedResource(principal: IConstruct): boolean { - // yes, this sucks - // this is just a temporary stopgap to stem the bleeding while we work on a proper fix - return principal instanceof iam.Role || - principal instanceof iam.User || - principal instanceof iam.Group; - } - private isGranteeFromAnotherRegion(grantee: iam.IGrantable): boolean { if (!isConstruct(grantee)) { return false; diff --git a/packages/@aws-cdk/aws-kms/test/key.test.ts b/packages/@aws-cdk/aws-kms/test/key.test.ts index a73d0b0cabf97..43d6fbdf82aaa 100644 --- a/packages/@aws-cdk/aws-kms/test/key.test.ts +++ b/packages/@aws-cdk/aws-kms/test/key.test.ts @@ -337,6 +337,41 @@ describe('key policies', () => { }); }); + testFutureBehavior('grant for an immutable role', flags, cdk.App, (app) => { + const principalStack = new cdk.Stack(app, 'PrincipalStack', { env: { account: '0123456789012' } }); + const principal = new iam.Role(principalStack, 'Role', { + assumedBy: new iam.AnyPrincipal(), + roleName: 'MyRolePhysicalName', + }); + + const keyStack = new cdk.Stack(app, 'KeyStack', { env: { account: '111111111111' } }); + const key = new kms.Key(keyStack, 'Key'); + principalStack.addDependency(keyStack); + key.grantEncrypt(principal.withoutPolicyUpdates()); + + Template.fromStack(keyStack).hasResourceProperties('AWS::KMS::Key', { + KeyPolicy: { + Statement: Match.arrayWith([{ + Action: 'kms:*', + Effect: 'Allow', + Principal: { AWS: { 'Fn::Join': ['', ['arn:', { Ref: 'AWS::Partition' }, ':iam::111111111111:root']] } }, + Resource: '*', + }, + { + Action: [ + 'kms:Encrypt', + 'kms:ReEncrypt*', + 'kms:GenerateDataKey*', + ], + Effect: 'Allow', + Principal: { AWS: { 'Fn::Join': ['', ['arn:', { Ref: 'AWS::Partition' }, ':iam::0123456789012:root']] } }, + Resource: '*', + }]), + Version: '2012-10-17', + }, + }); + }); + testFutureBehavior('additional key admins can be specified (with imported/immutable principal)', flags, cdk.App, (app) => { const stack = new cdk.Stack(app); const adminRole = iam.Role.fromRoleArn(stack, 'Admin', 'arn:aws:iam::123456789012:role/TrustedAdmin'); diff --git a/packages/@aws-cdk/core/lib/resource.ts b/packages/@aws-cdk/core/lib/resource.ts index 390205ed0e02f..9d407f4030ef2 100644 --- a/packages/@aws-cdk/core/lib/resource.ts +++ b/packages/@aws-cdk/core/lib/resource.ts @@ -123,10 +123,17 @@ export abstract class Resource extends Construct implements IResource { /** * Check whether the given construct is a Resource */ - public static isResource(construct: IConstruct): construct is CfnResource { + public static isResource(construct: IConstruct): construct is Resource { return construct !== null && typeof(construct) === 'object' && RESOURCE_SYMBOL in construct; } + /** + * Returns true if the construct was created by CDK, and false otherwise + */ + public static isOwnedResource(construct: IConstruct): boolean { + return construct.node.defaultChild ? CfnResource.isCfnResource(construct.node.defaultChild) : false; + } + public readonly stack: Stack; public readonly env: ResourceEnvironment; From bc70310624084099af013a8030f5a7cb873b6817 Mon Sep 17 00:00:00 2001 From: AWS CDK Team Date: Thu, 14 Jul 2022 18:31:20 +0000 Subject: [PATCH 54/92] chore(release): 2.32.0 --- CHANGELOG.v2.alpha.md | 18 ++++++++++++++++++ CHANGELOG.v2.md | 44 +++++++++++++++++++++++++++++++++++++++++++ version.v2.json | 4 ++-- 3 files changed, 64 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.v2.alpha.md b/CHANGELOG.v2.alpha.md index a63e797cb9131..c4f1e1c027355 100644 --- a/CHANGELOG.v2.alpha.md +++ b/CHANGELOG.v2.alpha.md @@ -2,6 +2,24 @@ All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. +## [2.32.0-alpha.0](https://github.com/aws/aws-cdk/compare/v2.31.2-alpha.0...v2.32.0-alpha.0) (2022-07-14) + + +### Features + +* **appsync:** set max batch size when using batch invoke ([#20995](https://github.com/aws/aws-cdk/issues/20995)) ([69d25a6](https://github.com/aws/aws-cdk/commit/69d25a6f26f03c6589b350803431de23fe598ae0)), closes [#20467](https://github.com/aws/aws-cdk/issues/20467) +* **batch:** add launchTemplateId in LaunchTemplateSpecification ([#20184](https://github.com/aws/aws-cdk/issues/20184)) ([269b8d0](https://github.com/aws/aws-cdk/commit/269b8d0ca737a1bad6736a2d5ed234602cd8f469)), closes [#20163](https://github.com/aws/aws-cdk/issues/20163) +* **glue:** enable partition filtering on tables ([#21081](https://github.com/aws/aws-cdk/issues/21081)) ([bf35048](https://github.com/aws/aws-cdk/commit/bf35048cc5f907c7226f60aa8b3b4b8b500d2bc0)), closes [#20825](https://github.com/aws/aws-cdk/issues/20825) +* **integ-tests:** expose adding IAM policies to the assertion provider ([#20769](https://github.com/aws/aws-cdk/issues/20769)) ([c2f40b7](https://github.com/aws/aws-cdk/commit/c2f40b7c97b822f258f953b572ba2e7a99403f89)) +* **neptune:** add engine version 1.1.1.0 ([#21079](https://github.com/aws/aws-cdk/issues/21079)) ([a113816](https://github.com/aws/aws-cdk/commit/a1138161ca295ad4a81fe32b51beb82438653144)), closes [#20869](https://github.com/aws/aws-cdk/issues/20869) +* **redshift:** adds classic or elastic resize type option ([#21084](https://github.com/aws/aws-cdk/issues/21084)) ([b5e9c1a](https://github.com/aws/aws-cdk/commit/b5e9c1a99be6898c544f91781ceb4ee1d371a03e)), closes [#19430](https://github.com/aws/aws-cdk/issues/19430) + + +### Bug Fixes + +* **appsync:** domain name api association fails when domain name creation is in the same stack ([#20173](https://github.com/aws/aws-cdk/issues/20173)) ([c1495f0](https://github.com/aws/aws-cdk/commit/c1495f0b700cedc04b556844397048ee41a7d891)), closes [#18395](https://github.com/aws/aws-cdk/issues/18395) +* **integ-runner:** test names change depending on the discovery directory ([#21093](https://github.com/aws/aws-cdk/issues/21093)) ([d38f78c](https://github.com/aws/aws-cdk/commit/d38f78c3fc9ba37b3a1033dabe89cd60dfd81b8f)) + ## [2.31.2-alpha.0](https://github.com/aws/aws-cdk/compare/v2.31.1-alpha.0...v2.31.2-alpha.0) (2022-07-13) ## [2.31.1-alpha.0](https://github.com/aws/aws-cdk/compare/v2.31.0-alpha.0...v2.31.1-alpha.0) (2022-07-08) diff --git a/CHANGELOG.v2.md b/CHANGELOG.v2.md index fef997ae1539c..144df0b1dcf0b 100644 --- a/CHANGELOG.v2.md +++ b/CHANGELOG.v2.md @@ -2,6 +2,50 @@ All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. +## [2.32.0](https://github.com/aws/aws-cdk/compare/v2.31.2...v2.32.0) (2022-07-14) + + +### Features + +* **backup:** support RDS database cluster and serverless cluster ([#17971](https://github.com/aws/aws-cdk/issues/17971)) ([53a6a47](https://github.com/aws/aws-cdk/commit/53a6a479bf7cd5ae58b29762cde8b371c03f7864)), closes [#16457](https://github.com/aws/aws-cdk/issues/16457) +* **backup:** vault lock ([#21105](https://github.com/aws/aws-cdk/issues/21105)) ([a25677b](https://github.com/aws/aws-cdk/commit/a25677bd6d3931b1b522f5ff0859693fe6dc855d)), closes [#21076](https://github.com/aws/aws-cdk/issues/21076) +* **cfnspec:** cloudformation spec v79.0.0 ([#21053](https://github.com/aws/aws-cdk/issues/21053)) ([68f09b7](https://github.com/aws/aws-cdk/commit/68f09b7c6f8e6c1ddf13fdd4e116d12333d24c46)) +* **cli:** --force flag and glob-style key matches for context --reset ([#19890](https://github.com/aws/aws-cdk/issues/19890)) ([39a7c1f](https://github.com/aws/aws-cdk/commit/39a7c1f0bafb1cf3f51fbe09053e443c0d87487e)), closes [#19840](https://github.com/aws/aws-cdk/issues/19840) [#19888](https://github.com/aws/aws-cdk/issues/19888) +* **codebuild:** add support for new codebuild images ([#20992](https://github.com/aws/aws-cdk/issues/20992)) ([9f3d71c](https://github.com/aws/aws-cdk/commit/9f3d71c622203d14f6763221a70e36a8a314393c)), closes [#20960](https://github.com/aws/aws-cdk/issues/20960) +* **core:** add a description parameter for the NestedStackProps ([#20930](https://github.com/aws/aws-cdk/issues/20930)) ([5ef106b](https://github.com/aws/aws-cdk/commit/5ef106b9fbfcaccb0d22b84feebc79b59ff7eea0)), closes [#16337](https://github.com/aws/aws-cdk/issues/16337) +* **ec2:** expose interface endpoint service shortname ([#20965](https://github.com/aws/aws-cdk/issues/20965)) ([ebfbf54](https://github.com/aws/aws-cdk/commit/ebfbf54cd669c6c4fc9f0dfa066e23730a171253)) +* **rds:** support rolling instance updates to reduce downtime ([#20054](https://github.com/aws/aws-cdk/issues/20054)) ([86790b6](https://github.com/aws/aws-cdk/commit/86790b632a997645970f310ac222fc52e3e58a47)), closes [#10595](https://github.com/aws/aws-cdk/issues/10595) [#10595](https://github.com/aws/aws-cdk/issues/10595) +* **secretsmanager:** create secret with secretObjectValue ([#21091](https://github.com/aws/aws-cdk/issues/21091)) ([5f0eff2](https://github.com/aws/aws-cdk/commit/5f0eff291b2cac6f2fbddfbe84d06f3a92f70c1d)), closes [#20461](https://github.com/aws/aws-cdk/issues/20461) +* **ses:** DedicatedIpPool, ConfigurationSet and EmailIdentity ([#20997](https://github.com/aws/aws-cdk/issues/20997)) ([541ce1b](https://github.com/aws/aws-cdk/commit/541ce1b46e5e21764d5f58ef73f46946bfd68cd7)) +* **stepfunctions-tasks:** support parameters in StepFunctionsInvokeActivity ([#21077](https://github.com/aws/aws-cdk/issues/21077)) ([10f8821](https://github.com/aws/aws-cdk/commit/10f8821275c4db0377d11662e1d14dff1dec2f5d)), closes [#21020](https://github.com/aws/aws-cdk/issues/21020) + + +### Bug Fixes + +* **apigateway:** serialization exception with step functions integration ([#20169](https://github.com/aws/aws-cdk/issues/20169)) ([6640338](https://github.com/aws/aws-cdk/commit/6640338823738017b35cdaa391243aa5782e0bcf)) +* **aws-ec2:** flow log destinationOptions requires all properties ([#21042](https://github.com/aws/aws-cdk/issues/21042)) ([0a76009](https://github.com/aws/aws-cdk/commit/0a76009cae7c89f3563ac56a8d19dfaf92f2a83f)), closes [#20765](https://github.com/aws/aws-cdk/issues/20765) [/docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-ec2-flowlog.html#cfn-ec2](https://github.com/aws//docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-ec2-flowlog.html/issues/cfn-ec2) [#21037](https://github.com/aws/aws-cdk/issues/21037) +* **aws-eks:** cap generated stack names at 128 characters ([#20528](https://github.com/aws/aws-cdk/issues/20528)) ([6e9963c](https://github.com/aws/aws-cdk/commit/6e9963c38e091b37a097f176eae2854ab907ae40)), closes [#20124](https://github.com/aws/aws-cdk/issues/20124) +* **cli:** `--no-fail` flag is ignored in favor of the `enableDiffNoFail` feature flag ([#21107](https://github.com/aws/aws-cdk/issues/21107)) ([cad6fc5](https://github.com/aws/aws-cdk/commit/cad6fc5f0f6d963152ede3101d36d085e399f99a)) +* **cli:** CLI errors when run as a non-existent user ([#21018](https://github.com/aws/aws-cdk/issues/21018)) ([e6015a9](https://github.com/aws/aws-cdk/commit/e6015a9ec857ad13cb1d71f8e0aa003e9327d49b)), closes [#7937](https://github.com/aws/aws-cdk/issues/7937) +* **cli:** format of tags in cdk.json is not validated ([#21050](https://github.com/aws/aws-cdk/issues/21050)) ([8e6a387](https://github.com/aws/aws-cdk/commit/8e6a387b2f8502a9a1c861d5bd1c5c7b0d3ada54)), closes [#20854](https://github.com/aws/aws-cdk/issues/20854) +* **cli:** revert "fix(cli): format of tags in cdk.json is not validated" ([#21092](https://github.com/aws/aws-cdk/issues/21092)) ([dd9f5c5](https://github.com/aws/aws-cdk/commit/dd9f5c5a008a8e1cc4c07511ad26658efa317992)), closes [aws/aws-cdk#21050](https://github.com/aws/aws-cdk/issues/21050) [#21050](https://github.com/aws/aws-cdk/issues/21050) +* **core:** entrypoint option never used ([#21124](https://github.com/aws/aws-cdk/issues/21124)) ([e123087](https://github.com/aws/aws-cdk/commit/e1230877d86fcec9bc3e18c0afce860dd08b33c2)) +* **core:** updatedProperties function name is misspelled ([#21071](https://github.com/aws/aws-cdk/issues/21071)) ([7b389f0](https://github.com/aws/aws-cdk/commit/7b389f0e6ab123622677efb48c550ca6050d18bc)) +* **core:** use node.path in skip bundling check for consistency with cdk deploy CLI ([#19950](https://github.com/aws/aws-cdk/issues/19950)) ([5cff2d9](https://github.com/aws/aws-cdk/commit/5cff2d9d28c4be0bb72b0febd3f30311252f57f8)), closes [#19927](https://github.com/aws/aws-cdk/issues/19927) [/github.com/aws/aws-cdk/blob/1d0270446b3effa6b8518de3c7d76f0c14e626c5/packages/aws-cdk/lib/api/cxapp/cloud-assembly.ts#L138](https://github.com/aws//github.com/aws/aws-cdk/blob/1d0270446b3effa6b8518de3c7d76f0c14e626c5/packages/aws-cdk/lib/api/cxapp/cloud-assembly.ts/issues/L138) [aws-cdk/cx-api/lib/cloud-artifact.ts#L143-L145](https://github.com/aws-cdk/cx-api/lib/cloud-artifact.ts/issues/L143-L145) [aws-cdk/core/lib/stack-synthesizers/_shared.ts#L66](https://github.com/aws-cdk/core/lib/stack-synthesizers/_shared.ts/issues/L66) +* **custom-resources:** Custom resource provider framework not passing `ResponseURL` to user function ([#21065](https://github.com/aws/aws-cdk/issues/21065)) ([0c31b7f](https://github.com/aws/aws-cdk/commit/0c31b7fed20a49cacd578d10020c5064af3e0db9)), closes [#21058](https://github.com/aws/aws-cdk/issues/21058) +* **custom-resources:** Custom resource provider framework not passing `ResponseURL` to user function ([#21117](https://github.com/aws/aws-cdk/issues/21117)) ([ddfca48](https://github.com/aws/aws-cdk/commit/ddfca48bb47d7bfe8b08827487e228aa4f149e43)), closes [aws#21065](https://github.com/aws/aws/issues/21065) [aws#21109](https://github.com/aws/aws/issues/21109) [aws#21058](https://github.com/aws/aws/issues/21058) +* **ec2:** deprecated `SubnetType` enums are treated incorrectly ([#21140](https://github.com/aws/aws-cdk/issues/21140)) ([0b5123a](https://github.com/aws/aws-cdk/commit/0b5123ada8c7dfdfe6f55ec8882b6fccc3a5168d)) +* **events-targets:** api destination target ignores pathParameterValues and queryStringParameters ([#21111](https://github.com/aws/aws-cdk/issues/21111)) ([8446c5c](https://github.com/aws/aws-cdk/commit/8446c5ceb7e400966e573bccaea40378541b0579)), closes [#21101](https://github.com/aws/aws-cdk/issues/21101) +* **iam:** `conditions` parameters accept array values ([#21009](https://github.com/aws/aws-cdk/issues/21009)) ([0aad6c9](https://github.com/aws/aws-cdk/commit/0aad6c988f434403eb2fd946d735d1d40b4a1ca7)), closes [#20974](https://github.com/aws/aws-cdk/issues/20974) +* **kms:** correctly recognize newly created resources ([#21143](https://github.com/aws/aws-cdk/issues/21143)) ([0cd83cc](https://github.com/aws/aws-cdk/commit/0cd83ccf6ec8d72f39cc4b8b066c8f4184174f90)), closes [#19881](https://github.com/aws/aws-cdk/issues/19881) +* **logs:** `ResourcePolicy` does not have a `defaultChild` ([#21039](https://github.com/aws/aws-cdk/issues/21039)) ([4076153](https://github.com/aws/aws-cdk/commit/4076153a1716a25db284c09521ace4b4233d1e43)) +* **pipelines:** cannot publish assets to more than 35 environments ([#21010](https://github.com/aws/aws-cdk/issues/21010)) ([4b4af84](https://github.com/aws/aws-cdk/commit/4b4af8475400390dfdeee709961d7ee885358142)) +* **pipelines:** reuseCrossRegionSupportStacks=true does not fail when existing pipeline is used ([#20423](https://github.com/aws/aws-cdk/issues/20423)) ([9c0ccca](https://github.com/aws/aws-cdk/commit/9c0ccca817ace858457717f07c29575cd231a461)) +* **route53:** publichostedzone import returns IHostedZone instead of IPublicHostedZone ([#21007](https://github.com/aws/aws-cdk/issues/21007)) ([588ddf1](https://github.com/aws/aws-cdk/commit/588ddf1b509029c70eaf60d0cd852bdc834a3caa)), closes [#21004](https://github.com/aws/aws-cdk/issues/21004) +* **sns-subscriptions:** restrict encryption of queue to only the respective sns topic (under feature flag) ([#20521](https://github.com/aws/aws-cdk/issues/20521)) ([4e0c80f](https://github.com/aws/aws-cdk/commit/4e0c80f89353731edc6d5f7aba6539a4f340296c)), closes [#20339](https://github.com/aws/aws-cdk/issues/20339) +* flowlog has no default child ([#21045](https://github.com/aws/aws-cdk/issues/21045)) ([b025abc](https://github.com/aws/aws-cdk/commit/b025abc43b483df958fdf886f5617aeaaffb85e3)) +* **triggers:** permissions race condition ([#19455](https://github.com/aws/aws-cdk/issues/19455)) ([8ebb81b](https://github.com/aws/aws-cdk/commit/8ebb81bc61153e0578ff9a31520615e62c745781)) + ## [2.31.2](https://github.com/aws/aws-cdk/compare/v2.31.1...v2.31.2) (2022-07-13) diff --git a/version.v2.json b/version.v2.json index 6a0892b49493b..0b8bbd761b515 100644 --- a/version.v2.json +++ b/version.v2.json @@ -1,4 +1,4 @@ { - "version": "2.31.2", - "alphaVersion": "2.31.2-alpha.0" + "version": "2.32.0", + "alphaVersion": "2.32.0-alpha.0" } \ No newline at end of file From cd4851a53b75768fc352bc6255b5e9b2af20cf74 Mon Sep 17 00:00:00 2001 From: Adam Ruka Date: Thu, 14 Jul 2022 11:42:40 -0700 Subject: [PATCH 55/92] feat(cli): allow diffing against a processed template (#19908) In some cases, using an unprocessed template with `CfnInclude` fails, most commonly because the unprocessed template can contain cycles between the resources. When this happens, customers need to include the processed template instead, but when that happens, the `diff` command returns the wrong results, because it compares against the original, and thus unprocessed, template. Add a new command-line switch to the `diff` command that allows comparing against the processed template, which gives the correct results in this case. ---- ### All Submissions: * [x] Have you followed the guidelines in our [Contributing guide?](https://github.com/aws/aws-cdk/blob/master/CONTRIBUTING.md) ### Adding new Unconventional Dependencies: * [ ] This PR adds new unconventional dependencies following the process described [here](https://github.com/aws/aws-cdk/blob/master/CONTRIBUTING.md/#adding-new-unconventional-dependencies) ### New Features * [ ] Have you added the new feature to an [integration test](https://github.com/aws/aws-cdk/blob/master/INTEGRATION_TESTS.md)? * [ ] Did you use `yarn integ` to deploy the infrastructure and generate the snapshot (i.e. `yarn integ` without `--dry-run`)? *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- .../@aws-cdk/cloudformation-include/README.md | 42 +++++++++++++++--- .../doc-images/processed-template.png | Bin 0 -> 92182 bytes .../doc-images/unprocessed-template.png | Bin 0 -> 89404 bytes .../lib/api/cloudformation-deployments.ts | 7 ++- .../aws-cdk/lib/api/nested-stack-helpers.ts | 36 ++++++++------- .../aws-cdk/lib/api/util/cloudformation.ts | 16 +++++-- packages/aws-cdk/lib/cdk-toolkit.ts | 10 ++++- packages/aws-cdk/lib/cli.ts | 4 +- .../aws-cdk/test/util/cloudformation.test.ts | 37 +++++++++++++-- 9 files changed, 119 insertions(+), 33 deletions(-) create mode 100644 packages/@aws-cdk/cloudformation-include/doc-images/processed-template.png create mode 100644 packages/@aws-cdk/cloudformation-include/doc-images/unprocessed-template.png diff --git a/packages/@aws-cdk/cloudformation-include/README.md b/packages/@aws-cdk/cloudformation-include/README.md index 560718ea1449c..6e10d290a5c67 100644 --- a/packages/@aws-cdk/cloudformation-include/README.md +++ b/packages/@aws-cdk/cloudformation-include/README.md @@ -71,10 +71,6 @@ radio buttons on the bottom pane. This will add all resources from `my-template.json` / `my-template.yaml` into the CDK application, preserving their original logical IDs from the template file. -Note that this including process will _not_ execute any -[CloudFormation transforms](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/template-macros.html) - -including the [Serverless transform](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/transform-aws-serverless.html). - Any resource from the included template can be retrieved by referring to it by its logical ID from the template. If you know the class of the CDK object that corresponds to that resource, you can cast the returned object to the correct type: @@ -118,7 +114,39 @@ role.addToPolicy(new iam.PolicyStatement({ })); ``` -### Converting L1 resources to L2 +## Migrating templates that use Transforms + +You can use this module to migrate templates that use +[CloudFormation transforms](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/template-macros.html) - +including the [Serverless transform](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/transform-aws-serverless.html). + +The CDK including process does not execute Transforms, +and the `cdk diff` command by default compares against the original +(meaning, unprocessed) template. +So, if you're downloading the template to include from the CloudFormation AWS Console, +make sure to download the unprocessed template +(the "View processed template" checkbox is left **unchecked**, which is the default): + +![unprocessed template in the CloudFormation AWS Console](doc-images/unprocessed-template.png) + +However, certain unprocessed templates can fail when used with the `CfnInclude` class. +The most common reason for the failure is that the unprocessed template can contain cycles between resources, +which get removed after the Transform is processed, +but is not allowed when being included (as pure CloudFormation does not permit cycles). + +When that happens, you should instead download the processed template from the CloudFormation AWS Console +(make sure the "View processed template" checkbox is **checked** in that case): + +![processed template in the CloudFormation AWS Console](doc-images/processed-template.png) + +When you include that processed template in your CDK application, +running `cdk diff` will now show a lot of differences with the deployed Stack, +because `cdk diff` uses the unprocessed template by default. +To alleviate that problem, you can pass the `--processed` switch to `cdk diff`, +which will make the diff command compare against the processed template of the deployed Stack, +which will give more precise results in this case. + +## Converting L1 resources to L2 The resources the `getResource` method returns are what the CDK calls [Layer 1 resources](https://docs.aws.amazon.com/cdk/latest/guide/cfn_layer.html#cfn_layer_cfn) @@ -127,7 +155,7 @@ However, in many places in the Construct Library, the CDK requires so-called Layer 2 resources, like `IBucket`. There are two ways of going from an L1 to an L2 resource. -#### Using`fromCfn*()` methods +### Using`fromCfn*()` methods This is the preferred method of converting an L1 resource to an L2. It works by invoking a static method of the class of the L2 resource @@ -191,7 +219,7 @@ and would throw an exception. In those cases, you need the use the second method of converting an L1 to an L2. -#### Using `from*Name/Arn/Attributes()` methods +### Using `from*Name/Arn/Attributes()` methods If the resource you need does not have a `fromCfn*()` method, or if it does, but it throws an exception for your particular L1, diff --git a/packages/@aws-cdk/cloudformation-include/doc-images/processed-template.png b/packages/@aws-cdk/cloudformation-include/doc-images/processed-template.png new file mode 100644 index 0000000000000000000000000000000000000000..aa023080c8072de7a5b3c22cfa473901ddddc67f GIT binary patch literal 92182 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

rO7NXPp0f>%V;U-*44$Gu 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 { + public async readCurrentTemplateWithNestedStacks( + rootStackArtifact: cxapi.CloudFormationStackArtifact, + retrieveProcessedTemplate: boolean = false, + ): Promise