diff --git a/CHANGELOG.md b/CHANGELOG.md index 6b54895aee553..4e69649d4564b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,59 @@ 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. +## [1.57.0](https://github.com/aws/aws-cdk/compare/v1.56.0...v1.57.0) (2020-08-07) + + +### ⚠ BREAKING CHANGES + +* **apigatewayv2:** The parameter for the method `bind()` on +`IHttpRouteIntegration` has changed to accept one of type +`HttpRouteIntegrationBindOptions`. The previous parameter +`IHttpRoute` is now a property inside the new parameter under +the key `route`. +* **eks:** The experimental `eks.Cluster` construct no longer supports setting `kubectlEnabled: false`. A temporary drop-in alternative is `eks.LegacyCluster`, but we have plans to completely remove support for it in an upcoming release since `eks.Cluster` has matured and should provide all the needed capabilities. Please comment on https://github.com/aws/aws-cdk/issues/9332 if there are use cases that are not supported by `eks.Cluster`. +* **eks:** endpoint access is configured to private and public by default instead of just public +* `lambda.Version` and `apigateway.Deployment` resources with auto-generated IDs will be replaced as we fixed a bug which ignored resource dependencies when generating these logical IDs. +* **core:** in unit tests, the `node.path` of constructs within stacks created the root of the tree via `new Stack()` will now have a prefix `Default/` which represents an implicit `App` root. + +Related: https://github.com/aws/aws-cdk-rfcs/issues/192 +* **cloudfront:** the property OriginBase.originId has been removed + +### Features + +* **apigateway:** additionalProperties in RestApi Model supports JsonSchema type ([#8848](https://github.com/aws/aws-cdk/issues/8848)) ([5e087e5](https://github.com/aws/aws-cdk/commit/5e087e5f3d59f931ceabebb290536a93b170522c)), closes [#8069](https://github.com/aws/aws-cdk/issues/8069) +* **apigateway:** configure endpoint types on SpecRestApi ([#9068](https://github.com/aws/aws-cdk/issues/9068)) ([7673e48](https://github.com/aws/aws-cdk/commit/7673e487e6358d1b345a138f016ac38b33315e4b)), closes [#9060](https://github.com/aws/aws-cdk/issues/9060) +* **apigateway:** import API keys ([#9155](https://github.com/aws/aws-cdk/issues/9155)) ([e3f6ae3](https://github.com/aws/aws-cdk/commit/e3f6ae3078799d3ff1c3a2f4a4ec19a82652b3e2)), closes [#8367](https://github.com/aws/aws-cdk/issues/8367) +* **appsync:** add x-ray parameter to AppSync ([#9389](https://github.com/aws/aws-cdk/issues/9389)) ([51921ad](https://github.com/aws/aws-cdk/commit/51921ade45840737f554dad066abfbbfc3b822b6)) +* **cloudfront:** add support for Origin Groups ([#9360](https://github.com/aws/aws-cdk/issues/9360)) ([11e146c](https://github.com/aws/aws-cdk/commit/11e146cb330ae036920c5cc1ab74225c0775a695)), closes [#9109](https://github.com/aws/aws-cdk/issues/9109) +* **cloudfront:** Behaviors support cached methods, compression, viewer protocol, and smooth streaming ([#9411](https://github.com/aws/aws-cdk/issues/9411)) ([2451fa9](https://github.com/aws/aws-cdk/commit/2451fa96f6a623b0634ba249bf6cc2a38da1dbbf)), closes [#7086](https://github.com/aws/aws-cdk/issues/7086) [#9107](https://github.com/aws/aws-cdk/issues/9107) +* **core:** implicit app for root stacks ([#9342](https://github.com/aws/aws-cdk/issues/9342)) ([1d85a9f](https://github.com/aws/aws-cdk/commit/1d85a9f16c87f51440ffbddd854aa5410b69fac7)) +* **core:** warn if an aspect was added via another aspect ([#8639](https://github.com/aws/aws-cdk/issues/8639)) ([9d7bef7](https://github.com/aws/aws-cdk/commit/9d7bef797f296c3e9f6f5dac6a4edf3139c2dfe2)) +* **eks:** default masters role ([#9464](https://github.com/aws/aws-cdk/issues/9464)) ([b80c271](https://github.com/aws/aws-cdk/commit/b80c2718055a19a72955e457397d6e812a21e53e)), closes [#9463](https://github.com/aws/aws-cdk/issues/9463) +* **eks:** deprecate "kubectlEnabled: false" ([#9454](https://github.com/aws/aws-cdk/issues/9454)) ([2791017](https://github.com/aws/aws-cdk/commit/27910175560f4e354aebab86e338b6a9190db4a5)), closes [#9332](https://github.com/aws/aws-cdk/issues/9332) +* **eks:** endpoint access customization ([#9095](https://github.com/aws/aws-cdk/issues/9095)) ([692864c](https://github.com/aws/aws-cdk/commit/692864cf4659ba84fdec9d8a298c185679076d38)), closes [#5220](https://github.com/aws/aws-cdk/issues/5220) [/github.com/aws/aws-cdk/pull/9095#issuecomment-665621701](https://github.com/aws//github.com/aws/aws-cdk/pull/9095/issues/issuecomment-665621701) +* **s3:** Introduce S3 Inventory ([#9102](https://github.com/aws/aws-cdk/issues/9102)) ([b0f359e](https://github.com/aws/aws-cdk/commit/b0f359eee99c100e6d33e00388c1a4bffe7baa6c)) + + +### Bug Fixes + +* **apigatewayv2:** cyclic dependency between HttpApi and the lambda function ([#9100](https://github.com/aws/aws-cdk/issues/9100)) ([7b29774](https://github.com/aws/aws-cdk/commit/7b297749bbe5d75f29f1aeb2652d095e3f2630e1)), closes [#9075](https://github.com/aws/aws-cdk/issues/9075) +* **athena:** WorkGroup tags corruption ([#9085](https://github.com/aws/aws-cdk/issues/9085)) ([b688913](https://github.com/aws/aws-cdk/commit/b688913b7534867c4cb584e491bf6e89437b48d9)), closes [#6936](https://github.com/aws/aws-cdk/issues/6936) +* **aws-lambda-python:** use cp instead of rsync ([#9355](https://github.com/aws/aws-cdk/issues/9355)) ([056bcaf](https://github.com/aws/aws-cdk/commit/056bcafa99aa4b741bf1e1d075fe8ab188c99c34)), closes [#9349](https://github.com/aws/aws-cdk/issues/9349) +* **cfn-include:** fails to load SAM resources ([#9442](https://github.com/aws/aws-cdk/issues/9442)) ([1de9dc8](https://github.com/aws/aws-cdk/commit/1de9dc86a7990e8bd7c026bde59a02ecf0582616)) +* **cfn-include:** no longer concatenate elements of Fn::Join without tokens ([#9476](https://github.com/aws/aws-cdk/issues/9476)) ([d038b61](https://github.com/aws/aws-cdk/commit/d038b61cd9b015b231911d4aaac131080b8b7b7c)) +* **core:** can't have multiple CfnRules in a Stack ([#9500](https://github.com/aws/aws-cdk/issues/9500)) ([76a7bfd](https://github.com/aws/aws-cdk/commit/76a7bfdf95c48a8d924d9363da2913240a5326f9)), closes [#8251](https://github.com/aws/aws-cdk/issues/8251) [#9485](https://github.com/aws/aws-cdk/issues/9485) +* **core:** docs for CfnMapping are not clear ([#9451](https://github.com/aws/aws-cdk/issues/9451)) ([c1e3c57](https://github.com/aws/aws-cdk/commit/c1e3c575ba67c0bf6d9fbea443fb1c80bcce7d67)), closes [#9432](https://github.com/aws/aws-cdk/issues/9432) +* **dynamodb:** allow using PhysicalName.GENERATE_IF_NEEDED as the Table name ([#9377](https://github.com/aws/aws-cdk/issues/9377)) ([8ab7b10](https://github.com/aws/aws-cdk/commit/8ab7b1062416adce1f2423c558bd3bfd714c5590)), closes [#9374](https://github.com/aws/aws-cdk/issues/9374) +* **ecs:** Scope-down IAM permissions for ECS drain ([#9502](https://github.com/aws/aws-cdk/issues/9502)) ([9fbeec3](https://github.com/aws/aws-cdk/commit/9fbeec3d7fe73ec870fe2de0e122b7714165f70e)) +* **ecs:** Scope-down IAM permissions on Cluster ASG ([#9493](https://github.com/aws/aws-cdk/issues/9493)) ([1670289](https://github.com/aws/aws-cdk/commit/16702898feacfe4f8c5ec323205362d6a0e36a97)) +* **ecs-patterns:** Adds missing option to secure ingress of ALB in Ap… ([#9434](https://github.com/aws/aws-cdk/issues/9434)) ([ba1427f](https://github.com/aws/aws-cdk/commit/ba1427f8510bc5c123012f6cfa1ca55d456efba7)) +* **lambda:** bundling docker image does not exist for Go runtime ([#9465](https://github.com/aws/aws-cdk/issues/9465)) ([7666d9b](https://github.com/aws/aws-cdk/commit/7666d9ba6b9a1212796636840fb7a1dffe41e4f3)), closes [#9435](https://github.com/aws/aws-cdk/issues/9435) + + +* **cloudfront:** remove the originId property from OriginBase ([#9380](https://github.com/aws/aws-cdk/issues/9380)) ([70b9f63](https://github.com/aws/aws-cdk/commit/70b9f63fa979c8c1d74ecdbd1f3c5bd248c5715f)) +* do not use "synthesize" and "prepare" in the cdk ([#9410](https://github.com/aws/aws-cdk/issues/9410)) ([e3ae645](https://github.com/aws/aws-cdk/commit/e3ae645f636a9f08566435799b7f55d50f5298bb)), closes [/github.com/aws/aws-cdk/pull/9410#issuecomment-668552361](https://github.com/aws//github.com/aws/aws-cdk/pull/9410/issues/issuecomment-668552361) + ## [1.56.0](https://github.com/aws/aws-cdk/compare/v1.55.0...v1.56.0) (2020-07-31) diff --git a/allowed-breaking-changes.txt b/allowed-breaking-changes.txt index fd44c1df2ce34..d85b698a59ce6 100644 --- a/allowed-breaking-changes.txt +++ b/allowed-breaking-changes.txt @@ -19,3 +19,6 @@ removed:@aws-cdk/cdk-assets-schema.FileDestination removed:@aws-cdk/cdk-assets-schema.FileSource removed:@aws-cdk/cdk-assets-schema.ManifestFile removed:@aws-cdk/cdk-assets-schema.FileAssetPackaging + +changed-type:@aws-cdk/aws-codedeploy.IServerDeploymentGroup.autoScalingGroups +changed-type:@aws-cdk/aws-codedeploy.ServerDeploymentGroup.autoScalingGroups diff --git a/lerna.json b/lerna.json index 855e51ed977ca..96e8ea36e52af 100644 --- a/lerna.json +++ b/lerna.json @@ -10,5 +10,5 @@ "tools/*" ], "rejectCycles": "true", - "version": "1.56.0" + "version": "1.57.0" } diff --git a/packages/@aws-cdk/aws-autoscaling/lib/auto-scaling-group.ts b/packages/@aws-cdk/aws-autoscaling/lib/auto-scaling-group.ts index 157298b8e10b8..07aa86ff45306 100644 --- a/packages/@aws-cdk/aws-autoscaling/lib/auto-scaling-group.ts +++ b/packages/@aws-cdk/aws-autoscaling/lib/auto-scaling-group.ts @@ -390,7 +390,9 @@ abstract class AutoScalingGroupBase extends Resource implements IAutoScalingGrou public abstract autoScalingGroupName: string; public abstract autoScalingGroupArn: string; + public abstract readonly osType: ec2.OperatingSystemType; protected albTargetGroup?: elbv2.ApplicationTargetGroup; + public readonly grantPrincipal: iam.IPrincipal = new iam.UnknownPrincipal({ resource: this }); /** * Send a message to either an SQS queue or SNS topic when instances launch or terminate @@ -490,6 +492,10 @@ abstract class AutoScalingGroupBase extends Resource implements IAutoScalingGrou public scaleOnMetric(id: string, props: BasicStepScalingPolicyProps): StepScalingPolicy { return new StepScalingPolicy(this, id, { ...props, autoScalingGroup: this }); } + + public addUserData(..._commands: string[]): void { + // do nothing + } } /** @@ -508,8 +514,7 @@ export class AutoScalingGroup extends AutoScalingGroupBase implements elb.ILoadBalancerTarget, ec2.IConnectable, elbv2.IApplicationLoadBalancerTarget, - elbv2.INetworkLoadBalancerTarget, - iam.IGrantable { + elbv2.INetworkLoadBalancerTarget { public static fromAutoScalingGroupName(scope: Construct, id: string, autoScalingGroupName: string): IAutoScalingGroup { class Import extends AutoScalingGroupBase { @@ -519,6 +524,7 @@ export class AutoScalingGroup extends AutoScalingGroupBase implements resource: 'autoScalingGroup:*:autoScalingGroupName', resourceName: this.autoScalingGroupName, }); + public readonly osType = ec2.OperatingSystemType.UNKNOWN; } return new Import(scope, id); @@ -760,11 +766,7 @@ export class AutoScalingGroup extends AutoScalingGroupBase implements return { targetType: elbv2.TargetType.INSTANCE }; } - /** - * Add command to the startup script of fleet instances. - * The command must be in the scripting language supported by the fleet's OS (i.e. Linux/Windows). - */ - public addUserData(...commands: string[]) { + public addUserData(...commands: string[]): void { this.userData.addCommands(...commands); } @@ -1121,7 +1123,7 @@ function validatePercentage(x?: number): number | undefined { /** * An AutoScalingGroup */ -export interface IAutoScalingGroup extends IResource { +export interface IAutoScalingGroup extends IResource, iam.IGrantable { /** * The name of the AutoScalingGroup * @attribute @@ -1134,6 +1136,19 @@ export interface IAutoScalingGroup extends IResource { */ readonly autoScalingGroupArn: string; + /** + * The operating system family that the instances in this auto-scaling group belong to. + * Is 'UNKNOWN' for imported ASGs. + */ + readonly osType: ec2.OperatingSystemType; + + /** + * Add command to the startup script of fleet instances. + * The command must be in the scripting language supported by the fleet's OS (i.e. Linux/Windows). + * Does nothing for imported ASGs. + */ + addUserData(...commands: string[]): void; + /** * Send a message to either an SQS queue or SNS topic when instances launch or terminate */ diff --git a/packages/@aws-cdk/aws-codedeploy/lib/server/deployment-group.ts b/packages/@aws-cdk/aws-codedeploy/lib/server/deployment-group.ts index 27f1b32fd3a46..3aa3f5915cded 100644 --- a/packages/@aws-cdk/aws-codedeploy/lib/server/deployment-group.ts +++ b/packages/@aws-cdk/aws-codedeploy/lib/server/deployment-group.ts @@ -24,7 +24,7 @@ export interface IServerDeploymentGroup extends cdk.IResource { */ readonly deploymentGroupArn: string; readonly deploymentConfig: IServerDeploymentConfig; - readonly autoScalingGroups?: autoscaling.AutoScalingGroup[]; + readonly autoScalingGroups?: autoscaling.IAutoScalingGroup[]; } /** @@ -69,7 +69,7 @@ abstract class ServerDeploymentGroupBase extends cdk.Resource implements IServer public abstract readonly deploymentGroupName: string; public abstract readonly deploymentGroupArn: string; public readonly deploymentConfig: IServerDeploymentConfig; - public abstract readonly autoScalingGroups?: autoscaling.AutoScalingGroup[]; + public abstract readonly autoScalingGroups?: autoscaling.IAutoScalingGroup[]; constructor(scope: cdk.Construct, id: string, deploymentConfig?: IServerDeploymentConfig, props?: cdk.ResourceProps) { super(scope, id, props); @@ -171,7 +171,7 @@ export interface ServerDeploymentGroupProps { * * @default [] */ - readonly autoScalingGroups?: autoscaling.AutoScalingGroup[]; + readonly autoScalingGroups?: autoscaling.IAutoScalingGroup[]; /** * If you've provided any auto-scaling groups with the {@link #autoScalingGroups} property, @@ -258,7 +258,7 @@ export class ServerDeploymentGroup extends ServerDeploymentGroupBase { public readonly deploymentGroupArn: string; public readonly deploymentGroupName: string; - private readonly _autoScalingGroups: autoscaling.AutoScalingGroup[]; + private readonly _autoScalingGroups: autoscaling.IAutoScalingGroup[]; private readonly installAgent: boolean; private readonly codeDeployBucket: s3.IBucket; private readonly alarms: cloudwatch.IAlarm[]; @@ -333,16 +333,16 @@ export class ServerDeploymentGroup extends ServerDeploymentGroupBase { this.alarms.push(alarm); } - public get autoScalingGroups(): autoscaling.AutoScalingGroup[] | undefined { + public get autoScalingGroups(): autoscaling.IAutoScalingGroup[] | undefined { return this._autoScalingGroups.slice(); } - private addCodeDeployAgentInstallUserData(asg: autoscaling.AutoScalingGroup): void { + private addCodeDeployAgentInstallUserData(asg: autoscaling.IAutoScalingGroup): void { if (!this.installAgent) { return; } - this.codeDeployBucket.grantRead(asg.role, 'latest/*'); + this.codeDeployBucket.grantRead(asg, 'latest/*'); switch (asg.osType) { case ec2.OperatingSystemType.LINUX: diff --git a/packages/@aws-cdk/aws-codepipeline-actions/lib/codedeploy/server-deploy-action.ts b/packages/@aws-cdk/aws-codepipeline-actions/lib/codedeploy/server-deploy-action.ts index 98606a8aac9a1..07c0662824481 100644 --- a/packages/@aws-cdk/aws-codepipeline-actions/lib/codedeploy/server-deploy-action.ts +++ b/packages/@aws-cdk/aws-codepipeline-actions/lib/codedeploy/server-deploy-action.ts @@ -58,7 +58,7 @@ export class CodeDeployServerDeployAction extends Action { // grant the ASG Role permissions to read from the Pipeline Bucket for (const asg of this.deploymentGroup.autoScalingGroups || []) { - options.bucket.grantRead(asg.role); + options.bucket.grantRead(asg); } // the Action's Role needs to read from the Bucket to get artifacts diff --git a/packages/@aws-cdk/aws-cognito/test/user-pool.test.ts b/packages/@aws-cdk/aws-cognito/test/user-pool.test.ts index 801879f6ea55a..cf24bed751e34 100644 --- a/packages/@aws-cdk/aws-cognito/test/user-pool.test.ts +++ b/packages/@aws-cdk/aws-cognito/test/user-pool.test.ts @@ -896,202 +896,203 @@ describe('User Pool', () => { }, }); }); -}); - -test('addClient', () => { - // GIVEN - const stack = new Stack(); - - // WHEN - const userpool = new UserPool(stack, 'Pool'); - userpool.addClient('UserPoolClient', { - userPoolClientName: 'userpoolclient', - }); - const imported = UserPool.fromUserPoolId(stack, 'imported', 'imported-userpool-id'); - imported.addClient('UserPoolImportedClient', { - userPoolClientName: 'userpoolimportedclient', - }); - - // THEN - expect(stack).toHaveResourceLike('AWS::Cognito::UserPoolClient', { - ClientName: 'userpoolclient', - UserPoolId: stack.resolve(userpool.userPoolId), - }); - expect(stack).toHaveResourceLike('AWS::Cognito::UserPoolClient', { - ClientName: 'userpoolimportedclient', - UserPoolId: stack.resolve(imported.userPoolId), - }); -}); - -test('addDomain', () => { - // GIVEN - const stack = new Stack(); - - // WHEN - const userpool = new UserPool(stack, 'Pool'); - userpool.addDomain('UserPoolDomain', { - cognitoDomain: { - domainPrefix: 'userpooldomain', - }, - }); - const imported = UserPool.fromUserPoolId(stack, 'imported', 'imported-userpool-id'); - imported.addDomain('UserPoolImportedDomain', { - cognitoDomain: { - domainPrefix: 'userpoolimporteddomain', - }, - }); - - // THEN - expect(stack).toHaveResourceLike('AWS::Cognito::UserPoolDomain', { - Domain: 'userpooldomain', - UserPoolId: stack.resolve(userpool.userPoolId), - }); - expect(stack).toHaveResourceLike('AWS::Cognito::UserPoolDomain', { - Domain: 'userpoolimporteddomain', - UserPoolId: stack.resolve(imported.userPoolId), - }); -}); - -test('registered identity providers', () => { - // GIVEN - const stack = new Stack(); - const userPool = new UserPool(stack, 'pool'); - const provider1 = UserPoolIdentityProvider.fromProviderName(stack, 'provider1', 'provider1'); - const provider2 = UserPoolIdentityProvider.fromProviderName(stack, 'provider2', 'provider2'); - - // WHEN - userPool.registerIdentityProvider(provider1); - userPool.registerIdentityProvider(provider2); - - // THEN - expect(userPool.identityProviders).toEqual([provider1, provider2]); -}); -function fooFunction(scope: Construct, name: string): lambda.IFunction { - return new lambda.Function(scope, name, { - functionName: name, - code: lambda.Code.inline('foo'), - runtime: lambda.Runtime.NODEJS_12_X, - handler: 'index.handler', - }); -} - -describe('AccountRecoverySetting should be configured correctly', () => { - test('EMAIL_AND_PHONE_WITHOUT_MFA', () => { + test('addClient', () => { // GIVEN const stack = new Stack(); // WHEN - new UserPool(stack, 'pool', { accountRecovery: AccountRecovery.EMAIL_AND_PHONE_WITHOUT_MFA }); + const userpool = new UserPool(stack, 'Pool'); + userpool.addClient('UserPoolClient', { + userPoolClientName: 'userpoolclient', + }); + const imported = UserPool.fromUserPoolId(stack, 'imported', 'imported-userpool-id'); + imported.addClient('UserPoolImportedClient', { + userPoolClientName: 'userpoolimportedclient', + }); // THEN - expect(stack).toHaveResource('AWS::Cognito::UserPool', { - AccountRecoverySetting: { - RecoveryMechanisms: [ - { Name: 'verified_email', Priority: 1 }, - { Name: 'verified_phone_number', Priority: 2 }, - ], - }, + expect(stack).toHaveResourceLike('AWS::Cognito::UserPoolClient', { + ClientName: 'userpoolclient', + UserPoolId: stack.resolve(userpool.userPoolId), + }); + expect(stack).toHaveResourceLike('AWS::Cognito::UserPoolClient', { + ClientName: 'userpoolimportedclient', + UserPoolId: stack.resolve(imported.userPoolId), }); }); - test('PHONE_WITHOUT_MFA_AND_EMAIL', () => { + test('addDomain', () => { // GIVEN const stack = new Stack(); // WHEN - new UserPool(stack, 'pool', { accountRecovery: AccountRecovery.PHONE_WITHOUT_MFA_AND_EMAIL }); + const userpool = new UserPool(stack, 'Pool'); + userpool.addDomain('UserPoolDomain', { + cognitoDomain: { + domainPrefix: 'userpooldomain', + }, + }); + const imported = UserPool.fromUserPoolId(stack, 'imported', 'imported-userpool-id'); + imported.addDomain('UserPoolImportedDomain', { + cognitoDomain: { + domainPrefix: 'userpoolimporteddomain', + }, + }); // THEN - expect(stack).toHaveResource('AWS::Cognito::UserPool', { - AccountRecoverySetting: { - RecoveryMechanisms: [ - { Name: 'verified_phone_number', Priority: 1 }, - { Name: 'verified_email', Priority: 2 }, - ], - }, + expect(stack).toHaveResourceLike('AWS::Cognito::UserPoolDomain', { + Domain: 'userpooldomain', + UserPoolId: stack.resolve(userpool.userPoolId), + }); + expect(stack).toHaveResourceLike('AWS::Cognito::UserPoolDomain', { + Domain: 'userpoolimporteddomain', + UserPoolId: stack.resolve(imported.userPoolId), }); }); - test('EMAIL_ONLY', () => { + test('registered identity providers', () => { // GIVEN const stack = new Stack(); + const userPool = new UserPool(stack, 'pool'); + const provider1 = UserPoolIdentityProvider.fromProviderName(stack, 'provider1', 'provider1'); + const provider2 = UserPoolIdentityProvider.fromProviderName(stack, 'provider2', 'provider2'); // WHEN - new UserPool(stack, 'pool', { accountRecovery: AccountRecovery.EMAIL_ONLY }); + userPool.registerIdentityProvider(provider1); + userPool.registerIdentityProvider(provider2); // THEN - expect(stack).toHaveResource('AWS::Cognito::UserPool', { - AccountRecoverySetting: { - RecoveryMechanisms: [ - { Name: 'verified_email', Priority: 1 }, - ], - }, - }); + expect(userPool.identityProviders).toEqual([provider1, provider2]); }); - test('PHONE_ONLY_WITHOUT_MFA', () => { - // GIVEN - const stack = new Stack(); + describe('AccountRecoverySetting should be configured correctly', () => { + test('EMAIL_AND_PHONE_WITHOUT_MFA', () => { + // GIVEN + const stack = new Stack(); + + // WHEN + new UserPool(stack, 'pool', { accountRecovery: AccountRecovery.EMAIL_AND_PHONE_WITHOUT_MFA }); + + // THEN + expect(stack).toHaveResource('AWS::Cognito::UserPool', { + AccountRecoverySetting: { + RecoveryMechanisms: [ + { Name: 'verified_email', Priority: 1 }, + { Name: 'verified_phone_number', Priority: 2 }, + ], + }, + }); + }); - // WHEN - new UserPool(stack, 'pool', { accountRecovery: AccountRecovery.PHONE_ONLY_WITHOUT_MFA }); + test('PHONE_WITHOUT_MFA_AND_EMAIL', () => { + // GIVEN + const stack = new Stack(); - // THEN - expect(stack).toHaveResource('AWS::Cognito::UserPool', { - AccountRecoverySetting: { - RecoveryMechanisms: [ - { Name: 'verified_phone_number', Priority: 1 }, - ], - }, + // WHEN + new UserPool(stack, 'pool', { accountRecovery: AccountRecovery.PHONE_WITHOUT_MFA_AND_EMAIL }); + + // THEN + expect(stack).toHaveResource('AWS::Cognito::UserPool', { + AccountRecoverySetting: { + RecoveryMechanisms: [ + { Name: 'verified_phone_number', Priority: 1 }, + { Name: 'verified_email', Priority: 2 }, + ], + }, + }); }); - }); - test('NONE', () => { - // GIVEN - const stack = new Stack(); + test('EMAIL_ONLY', () => { + // GIVEN + const stack = new Stack(); - // WHEN - new UserPool(stack, 'pool', { accountRecovery: AccountRecovery.NONE }); + // WHEN + new UserPool(stack, 'pool', { accountRecovery: AccountRecovery.EMAIL_ONLY }); - // THEN - expect(stack).toHaveResource('AWS::Cognito::UserPool', { - AccountRecoverySetting: { - RecoveryMechanisms: [ - { Name: 'admin_only', Priority: 1 }, - ], - }, + // THEN + expect(stack).toHaveResource('AWS::Cognito::UserPool', { + AccountRecoverySetting: { + RecoveryMechanisms: [ + { Name: 'verified_email', Priority: 1 }, + ], + }, + }); }); - }); - test('PHONE_AND_EMAIL', () => { - // GIVEN - const stack = new Stack(); + test('PHONE_ONLY_WITHOUT_MFA', () => { + // GIVEN + const stack = new Stack(); - // WHEN - new UserPool(stack, 'pool', { accountRecovery: AccountRecovery.PHONE_AND_EMAIL }); + // WHEN + new UserPool(stack, 'pool', { accountRecovery: AccountRecovery.PHONE_ONLY_WITHOUT_MFA }); - // THEN - expect(stack).toHaveResource('AWS::Cognito::UserPool', { - AccountRecoverySetting: ABSENT, + // THEN + expect(stack).toHaveResource('AWS::Cognito::UserPool', { + AccountRecoverySetting: { + RecoveryMechanisms: [ + { Name: 'verified_phone_number', Priority: 1 }, + ], + }, + }); }); - }); - test('default', () => { - // GIVEN - const stack = new Stack(); + test('NONE', () => { + // GIVEN + const stack = new Stack(); - // WHEN - new UserPool(stack, 'pool'); + // WHEN + new UserPool(stack, 'pool', { accountRecovery: AccountRecovery.NONE }); - // THEN - expect(stack).toHaveResource('AWS::Cognito::UserPool', { - AccountRecoverySetting: { - RecoveryMechanisms: [ - { Name: 'verified_phone_number', Priority: 1 }, - { Name: 'verified_email', Priority: 2 }, - ], - }, + // THEN + expect(stack).toHaveResource('AWS::Cognito::UserPool', { + AccountRecoverySetting: { + RecoveryMechanisms: [ + { Name: 'admin_only', Priority: 1 }, + ], + }, + }); + }); + + test('PHONE_AND_EMAIL', () => { + // GIVEN + const stack = new Stack(); + + // WHEN + new UserPool(stack, 'pool', { accountRecovery: AccountRecovery.PHONE_AND_EMAIL }); + + // THEN + expect(stack).toHaveResource('AWS::Cognito::UserPool', { + AccountRecoverySetting: ABSENT, + }); + }); + + test('default', () => { + // GIVEN + const stack = new Stack(); + + // WHEN + new UserPool(stack, 'pool'); + + // THEN + expect(stack).toHaveResource('AWS::Cognito::UserPool', { + AccountRecoverySetting: { + RecoveryMechanisms: [ + { Name: 'verified_phone_number', Priority: 1 }, + { Name: 'verified_email', Priority: 2 }, + ], + }, + }); }); }); }); + + +function fooFunction(scope: Construct, name: string): lambda.IFunction { + return new lambda.Function(scope, name, { + functionName: name, + code: lambda.Code.inline('foo'), + runtime: lambda.Runtime.NODEJS_12_X, + handler: 'index.handler', + }); +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-ec2/lib/machine-image.ts b/packages/@aws-cdk/aws-ec2/lib/machine-image.ts index c00326888faf8..77834eecac7c1 100644 --- a/packages/@aws-cdk/aws-ec2/lib/machine-image.ts +++ b/packages/@aws-cdk/aws-ec2/lib/machine-image.ts @@ -411,6 +411,11 @@ export class GenericWindowsImage implements IMachineImage { export enum OperatingSystemType { LINUX, WINDOWS, + /** + * Used when the type of the operating system is not known + * (for example, for imported Auto-Scaling Groups). + */ + UNKNOWN, } /** diff --git a/packages/@aws-cdk/aws-ec2/lib/user-data.ts b/packages/@aws-cdk/aws-ec2/lib/user-data.ts index d82754f9e17a8..617563e6cd05a 100644 --- a/packages/@aws-cdk/aws-ec2/lib/user-data.ts +++ b/packages/@aws-cdk/aws-ec2/lib/user-data.ts @@ -89,6 +89,7 @@ export abstract class UserData { switch (os) { case OperatingSystemType.LINUX: return UserData.forLinux(); case OperatingSystemType.WINDOWS: return UserData.forWindows(); + case OperatingSystemType.UNKNOWN: throw new Error('Cannot determine UserData for unknown operating system type'); } } @@ -275,4 +276,4 @@ class CustomUserData extends UserData { public addSignalOnExitCommand(): void { throw new Error('CustomUserData does not support addSignalOnExitCommand, use UserData.forLinux() or UserData.forWindows() instead.'); } -} \ No newline at end of file +} diff --git a/packages/@aws-cdk/aws-ecs-patterns/test/ec2/integ.multiple-application-load-balanced-ecs-service.expected.json b/packages/@aws-cdk/aws-ecs-patterns/test/ec2/integ.multiple-application-load-balanced-ecs-service.expected.json index 9af53dfb65b39..7afecdb128746 100644 --- a/packages/@aws-cdk/aws-ecs-patterns/test/ec2/integ.multiple-application-load-balanced-ecs-service.expected.json +++ b/packages/@aws-cdk/aws-ecs-patterns/test/ec2/integ.multiple-application-load-balanced-ecs-service.expected.json @@ -441,13 +441,39 @@ "Statement": [ { "Action": [ - "ecs:CreateCluster", "ecs:DeregisterContainerInstance", - "ecs:DiscoverPollEndpoint", - "ecs:Poll", "ecs:RegisterContainerInstance", - "ecs:StartTelemetrySession", - "ecs:Submit*", + "ecs:Submit*" + ], + "Effect": "Allow", + "Resource": { + "Fn::GetAtt": [ + "ClusterEB0386A7", + "Arn" + ] + } + }, + { + "Action": [ + "ecs:Poll", + "ecs:StartTelemetrySession" + ], + "Effect": "Allow", + "Resource": "*", + "Condition": { + "ArnEquals": { + "ecs:cluster": { + "Fn::GetAtt": [ + "ClusterEB0386A7", + "Arn" + ] + } + } + } + }, + { + "Action": [ + "ecs:DiscoverPollEndpoint", "ecr:GetAuthorizationToken", "logs:CreateLogStream", "logs:PutLogEvents" @@ -632,7 +658,17 @@ "ecs:DescribeTasks" ], "Effect": "Allow", - "Resource": "*" + "Resource": "*", + "Condition": { + "ArnEquals": { + "ecs:cluster": { + "Fn::GetAtt": [ + "ClusterEB0386A7", + "Arn" + ] + } + } + } }, { "Action": [ diff --git a/packages/@aws-cdk/aws-ecs-patterns/test/ec2/integ.scheduled-ecs-task.lit.expected.json b/packages/@aws-cdk/aws-ecs-patterns/test/ec2/integ.scheduled-ecs-task.lit.expected.json index 7cddaef40ca08..e9b8c11f8754a 100644 --- a/packages/@aws-cdk/aws-ecs-patterns/test/ec2/integ.scheduled-ecs-task.lit.expected.json +++ b/packages/@aws-cdk/aws-ecs-patterns/test/ec2/integ.scheduled-ecs-task.lit.expected.json @@ -261,21 +261,46 @@ "Statement": [ { "Action": [ - "ecs:CreateCluster", "ecs:DeregisterContainerInstance", - "ecs:DiscoverPollEndpoint", - "ecs:Poll", "ecs:RegisterContainerInstance", - "ecs:StartTelemetrySession", - "ecs:Submit*", + "ecs:Submit*" + ], + "Effect": "Allow", + "Resource": { + "Fn::GetAtt": [ + "EcsCluster97242B84", + "Arn" + ] + } + }, + { + "Action": [ + "ecs:Poll", + "ecs:StartTelemetrySession" + ], + "Effect": "Allow", + "Resource": "*", + "Condition": { + "ArnEquals": { + "ecs:cluster": { + "Fn::GetAtt": [ + "EcsCluster97242B84", + "Arn" + ] + } + } + } + }, + { + "Action": [ + "ecs:DiscoverPollEndpoint", "ecr:GetAuthorizationToken", "logs:CreateLogStream", "logs:PutLogEvents" ], "Effect": "Allow", "Resource": "*" - } - ], + } ], "Version": "2012-10-17" }, "PolicyName": "EcsClusterDefaultAutoScalingGroupInstanceRoleDefaultPolicy04DC6C80", @@ -449,7 +474,17 @@ "ecs:DescribeTasks" ], "Effect": "Allow", - "Resource": "*" + "Resource": "*", + "Condition": { + "ArnEquals": { + "ecs:cluster": { + "Fn::GetAtt": [ + "EcsCluster97242B84", + "Arn" + ] + } + } + } }, { "Action": [ diff --git a/packages/@aws-cdk/aws-ecs/lib/cluster.ts b/packages/@aws-cdk/aws-ecs/lib/cluster.ts index a16f4410af706..0fff4fd87bd93 100644 --- a/packages/@aws-cdk/aws-ecs/lib/cluster.ts +++ b/packages/@aws-cdk/aws-ecs/lib/cluster.ts @@ -212,16 +212,41 @@ export class Cluster extends Resource implements ICluster { // ECS instances must be able to do these things // Source: https://docs.aws.amazon.com/AmazonECS/latest/developerguide/instance_IAM_role.html + // But, scoped down to minimal permissions required. + // Notes: + // - 'ecs:CreateCluster' removed. The cluster already exists. autoScalingGroup.addToRolePolicy(new iam.PolicyStatement({ actions: [ - 'ecs:CreateCluster', 'ecs:DeregisterContainerInstance', - 'ecs:DiscoverPollEndpoint', - 'ecs:Poll', 'ecs:RegisterContainerInstance', - 'ecs:StartTelemetrySession', 'ecs:Submit*', + ], + resources: [ + this.clusterArn, + ], + })); + autoScalingGroup.addToRolePolicy(new iam.PolicyStatement({ + actions: [ + // These act on a cluster instance, and the instance doesn't exist until the service starts. + // Thus, scope to the cluster using a condition. + // See: https://docs.aws.amazon.com/IAM/latest/UserGuide/list_amazonelasticcontainerservice.html + 'ecs:Poll', + 'ecs:StartTelemetrySession', + ], + resources: ['*'], + conditions: { + ArnEquals: { 'ecs:cluster': this.clusterArn }, + }, + })); + autoScalingGroup.addToRolePolicy(new iam.PolicyStatement({ + actions: [ + // These do not support resource constraints, and must be resource '*' + 'ecs:DiscoverPollEndpoint', 'ecr:GetAuthorizationToken', + // Preserved for backwards compatibility. + // Users are able to enable cloudwatch agent using CDK. Existing + // customers might be installing CW agent as part of user-data so if we + // remove these permissions we will break that customer use cases. 'logs:CreateLogStream', 'logs:PutLogEvents', ], diff --git a/packages/@aws-cdk/aws-ecs/lib/drain-hook/instance-drain-hook.ts b/packages/@aws-cdk/aws-ecs/lib/drain-hook/instance-drain-hook.ts index 34f402fb97ab1..f8062befb60d4 100644 --- a/packages/@aws-cdk/aws-ecs/lib/drain-hook/instance-drain-hook.ts +++ b/packages/@aws-cdk/aws-ecs/lib/drain-hook/instance-drain-hook.ts @@ -90,6 +90,9 @@ export class InstanceDrainHook extends cdk.Construct { fn.addToRolePolicy(new iam.PolicyStatement({ actions: ['ecs:DescribeContainerInstances', 'ecs:DescribeTasks'], resources: ['*'], + conditions: { + ArnEquals: { 'ecs:cluster': props.cluster.clusterArn }, + }, })); // Restrict to the ECS Cluster diff --git a/packages/@aws-cdk/aws-ecs/test/ec2/integ.app-mesh-proxy-config.expected.json b/packages/@aws-cdk/aws-ecs/test/ec2/integ.app-mesh-proxy-config.expected.json index c644c86649ef6..24784b1521a45 100644 --- a/packages/@aws-cdk/aws-ecs/test/ec2/integ.app-mesh-proxy-config.expected.json +++ b/packages/@aws-cdk/aws-ecs/test/ec2/integ.app-mesh-proxy-config.expected.json @@ -420,13 +420,39 @@ "Statement": [ { "Action": [ - "ecs:CreateCluster", "ecs:DeregisterContainerInstance", - "ecs:DiscoverPollEndpoint", - "ecs:Poll", "ecs:RegisterContainerInstance", - "ecs:StartTelemetrySession", - "ecs:Submit*", + "ecs:Submit*" + ], + "Effect": "Allow", + "Resource": { + "Fn::GetAtt": [ + "EcsCluster97242B84", + "Arn" + ] + } + }, + { + "Action": [ + "ecs:Poll", + "ecs:StartTelemetrySession" + ], + "Effect": "Allow", + "Resource": "*", + "Condition": { + "ArnEquals": { + "ecs:cluster": { + "Fn::GetAtt": [ + "EcsCluster97242B84", + "Arn" + ] + } + } + } + }, + { + "Action": [ + "ecs:DiscoverPollEndpoint", "ecr:GetAuthorizationToken", "logs:CreateLogStream", "logs:PutLogEvents" @@ -611,7 +637,17 @@ "ecs:DescribeTasks" ], "Effect": "Allow", - "Resource": "*" + "Resource": "*", + "Condition": { + "ArnEquals": { + "ecs:cluster": { + "Fn::GetAtt": [ + "EcsCluster97242B84", + "Arn" + ] + } + } + } }, { "Action": [ diff --git a/packages/@aws-cdk/aws-ecs/test/ec2/integ.clb-host-nw.expected.json b/packages/@aws-cdk/aws-ecs/test/ec2/integ.clb-host-nw.expected.json index edb849319d5b8..80111c4451470 100644 --- a/packages/@aws-cdk/aws-ecs/test/ec2/integ.clb-host-nw.expected.json +++ b/packages/@aws-cdk/aws-ecs/test/ec2/integ.clb-host-nw.expected.json @@ -441,13 +441,39 @@ "Statement": [ { "Action": [ - "ecs:CreateCluster", "ecs:DeregisterContainerInstance", - "ecs:DiscoverPollEndpoint", - "ecs:Poll", "ecs:RegisterContainerInstance", - "ecs:StartTelemetrySession", - "ecs:Submit*", + "ecs:Submit*" + ], + "Effect": "Allow", + "Resource": { + "Fn::GetAtt": [ + "EcsCluster97242B84", + "Arn" + ] + } + }, + { + "Action": [ + "ecs:Poll", + "ecs:StartTelemetrySession" + ], + "Effect": "Allow", + "Resource": "*", + "Condition": { + "ArnEquals": { + "ecs:cluster": { + "Fn::GetAtt": [ + "EcsCluster97242B84", + "Arn" + ] + } + } + } + }, + { + "Action": [ + "ecs:DiscoverPollEndpoint", "ecr:GetAuthorizationToken", "logs:CreateLogStream", "logs:PutLogEvents" @@ -632,7 +658,17 @@ "ecs:DescribeTasks" ], "Effect": "Allow", - "Resource": "*" + "Resource": "*", + "Condition": { + "ArnEquals": { + "ecs:cluster": { + "Fn::GetAtt": [ + "EcsCluster97242B84", + "Arn" + ] + } + } + } }, { "Action": [ diff --git a/packages/@aws-cdk/aws-ecs/test/ec2/integ.firelens-s3-config.expected.json b/packages/@aws-cdk/aws-ecs/test/ec2/integ.firelens-s3-config.expected.json index a379d7c8c3109..ba3354e59a7ef 100644 --- a/packages/@aws-cdk/aws-ecs/test/ec2/integ.firelens-s3-config.expected.json +++ b/packages/@aws-cdk/aws-ecs/test/ec2/integ.firelens-s3-config.expected.json @@ -420,13 +420,39 @@ "Statement": [ { "Action": [ - "ecs:CreateCluster", "ecs:DeregisterContainerInstance", - "ecs:DiscoverPollEndpoint", - "ecs:Poll", "ecs:RegisterContainerInstance", - "ecs:StartTelemetrySession", - "ecs:Submit*", + "ecs:Submit*" + ], + "Effect": "Allow", + "Resource": { + "Fn::GetAtt": [ + "EcsCluster97242B84", + "Arn" + ] + } + }, + { + "Action": [ + "ecs:Poll", + "ecs:StartTelemetrySession" + ], + "Effect": "Allow", + "Resource": "*", + "Condition": { + "ArnEquals": { + "ecs:cluster": { + "Fn::GetAtt": [ + "EcsCluster97242B84", + "Arn" + ] + } + } + } + }, + { + "Action": [ + "ecs:DiscoverPollEndpoint", "ecr:GetAuthorizationToken", "logs:CreateLogStream", "logs:PutLogEvents" @@ -611,7 +637,17 @@ "ecs:DescribeTasks" ], "Effect": "Allow", - "Resource": "*" + "Resource": "*", + "Condition": { + "ArnEquals": { + "ecs:cluster": { + "Fn::GetAtt": [ + "EcsCluster97242B84", + "Arn" + ] + } + } + } }, { "Action": [ diff --git a/packages/@aws-cdk/aws-ecs/test/ec2/integ.lb-awsvpc-nw.expected.json b/packages/@aws-cdk/aws-ecs/test/ec2/integ.lb-awsvpc-nw.expected.json index e5e7464767c36..20b2143e7ebfa 100644 --- a/packages/@aws-cdk/aws-ecs/test/ec2/integ.lb-awsvpc-nw.expected.json +++ b/packages/@aws-cdk/aws-ecs/test/ec2/integ.lb-awsvpc-nw.expected.json @@ -420,13 +420,39 @@ "Statement": [ { "Action": [ - "ecs:CreateCluster", "ecs:DeregisterContainerInstance", - "ecs:DiscoverPollEndpoint", - "ecs:Poll", "ecs:RegisterContainerInstance", - "ecs:StartTelemetrySession", - "ecs:Submit*", + "ecs:Submit*" + ], + "Effect": "Allow", + "Resource": { + "Fn::GetAtt": [ + "EcsCluster97242B84", + "Arn" + ] + } + }, + { + "Action": [ + "ecs:Poll", + "ecs:StartTelemetrySession" + ], + "Effect": "Allow", + "Resource": "*", + "Condition": { + "ArnEquals": { + "ecs:cluster": { + "Fn::GetAtt": [ + "EcsCluster97242B84", + "Arn" + ] + } + } + } + }, + { + "Action": [ + "ecs:DiscoverPollEndpoint", "ecr:GetAuthorizationToken", "logs:CreateLogStream", "logs:PutLogEvents" @@ -611,7 +637,17 @@ "ecs:DescribeTasks" ], "Effect": "Allow", - "Resource": "*" + "Resource": "*", + "Condition": { + "ArnEquals": { + "ecs:cluster": { + "Fn::GetAtt": [ + "EcsCluster97242B84", + "Arn" + ] + } + } + } }, { "Action": [ diff --git a/packages/@aws-cdk/aws-ecs/test/ec2/integ.lb-bridge-nw.expected.json b/packages/@aws-cdk/aws-ecs/test/ec2/integ.lb-bridge-nw.expected.json index 74b289cadbde6..ae18cb651155e 100644 --- a/packages/@aws-cdk/aws-ecs/test/ec2/integ.lb-bridge-nw.expected.json +++ b/packages/@aws-cdk/aws-ecs/test/ec2/integ.lb-bridge-nw.expected.json @@ -441,13 +441,39 @@ "Statement": [ { "Action": [ - "ecs:CreateCluster", "ecs:DeregisterContainerInstance", - "ecs:DiscoverPollEndpoint", - "ecs:Poll", "ecs:RegisterContainerInstance", - "ecs:StartTelemetrySession", - "ecs:Submit*", + "ecs:Submit*" + ], + "Effect": "Allow", + "Resource": { + "Fn::GetAtt": [ + "EcsCluster97242B84", + "Arn" + ] + } + }, + { + "Action": [ + "ecs:Poll", + "ecs:StartTelemetrySession" + ], + "Effect": "Allow", + "Resource": "*", + "Condition": { + "ArnEquals": { + "ecs:cluster": { + "Fn::GetAtt": [ + "EcsCluster97242B84", + "Arn" + ] + } + } + } + }, + { + "Action": [ + "ecs:DiscoverPollEndpoint", "ecr:GetAuthorizationToken", "logs:CreateLogStream", "logs:PutLogEvents" @@ -632,7 +658,17 @@ "ecs:DescribeTasks" ], "Effect": "Allow", - "Resource": "*" + "Resource": "*", + "Condition": { + "ArnEquals": { + "ecs:cluster": { + "Fn::GetAtt": [ + "EcsCluster97242B84", + "Arn" + ] + } + } + } }, { "Action": [ diff --git a/packages/@aws-cdk/aws-ecs/test/ec2/integ.sd-awsvpc-nw.expected.json b/packages/@aws-cdk/aws-ecs/test/ec2/integ.sd-awsvpc-nw.expected.json index 46f3b9a4e26cf..66d961bceb04b 100644 --- a/packages/@aws-cdk/aws-ecs/test/ec2/integ.sd-awsvpc-nw.expected.json +++ b/packages/@aws-cdk/aws-ecs/test/ec2/integ.sd-awsvpc-nw.expected.json @@ -420,13 +420,39 @@ "Statement": [ { "Action": [ - "ecs:CreateCluster", "ecs:DeregisterContainerInstance", - "ecs:DiscoverPollEndpoint", - "ecs:Poll", "ecs:RegisterContainerInstance", - "ecs:StartTelemetrySession", - "ecs:Submit*", + "ecs:Submit*" + ], + "Effect": "Allow", + "Resource": { + "Fn::GetAtt": [ + "EcsCluster97242B84", + "Arn" + ] + } + }, + { + "Action": [ + "ecs:Poll", + "ecs:StartTelemetrySession" + ], + "Effect": "Allow", + "Resource": "*", + "Condition": { + "ArnEquals": { + "ecs:cluster": { + "Fn::GetAtt": [ + "EcsCluster97242B84", + "Arn" + ] + } + } + } + }, + { + "Action": [ + "ecs:DiscoverPollEndpoint", "ecr:GetAuthorizationToken", "logs:CreateLogStream", "logs:PutLogEvents" @@ -611,7 +637,17 @@ "ecs:DescribeTasks" ], "Effect": "Allow", - "Resource": "*" + "Resource": "*", + "Condition": { + "ArnEquals": { + "ecs:cluster": { + "Fn::GetAtt": [ + "EcsCluster97242B84", + "Arn" + ] + } + } + } }, { "Action": [ diff --git a/packages/@aws-cdk/aws-ecs/test/ec2/integ.sd-bridge-nw.expected.json b/packages/@aws-cdk/aws-ecs/test/ec2/integ.sd-bridge-nw.expected.json index 16d9538eb2127..2214c69632d98 100644 --- a/packages/@aws-cdk/aws-ecs/test/ec2/integ.sd-bridge-nw.expected.json +++ b/packages/@aws-cdk/aws-ecs/test/ec2/integ.sd-bridge-nw.expected.json @@ -420,13 +420,39 @@ "Statement": [ { "Action": [ - "ecs:CreateCluster", "ecs:DeregisterContainerInstance", - "ecs:DiscoverPollEndpoint", - "ecs:Poll", "ecs:RegisterContainerInstance", - "ecs:StartTelemetrySession", - "ecs:Submit*", + "ecs:Submit*" + ], + "Effect": "Allow", + "Resource": { + "Fn::GetAtt": [ + "EcsCluster97242B84", + "Arn" + ] + } + }, + { + "Action": [ + "ecs:Poll", + "ecs:StartTelemetrySession" + ], + "Effect": "Allow", + "Resource": "*", + "Condition": { + "ArnEquals": { + "ecs:cluster": { + "Fn::GetAtt": [ + "EcsCluster97242B84", + "Arn" + ] + } + } + } + }, + { + "Action": [ + "ecs:DiscoverPollEndpoint", "ecr:GetAuthorizationToken", "logs:CreateLogStream", "logs:PutLogEvents" @@ -611,7 +637,17 @@ "ecs:DescribeTasks" ], "Effect": "Allow", - "Resource": "*" + "Resource": "*", + "Condition": { + "ArnEquals": { + "ecs:cluster": { + "Fn::GetAtt": [ + "EcsCluster97242B84", + "Arn" + ] + } + } + } }, { "Action": [ diff --git a/packages/@aws-cdk/aws-ecs/test/ec2/integ.spot-drain.expected.json b/packages/@aws-cdk/aws-ecs/test/ec2/integ.spot-drain.expected.json index 71a61e7a4a4a7..3c78f85f86425 100644 --- a/packages/@aws-cdk/aws-ecs/test/ec2/integ.spot-drain.expected.json +++ b/packages/@aws-cdk/aws-ecs/test/ec2/integ.spot-drain.expected.json @@ -420,13 +420,39 @@ "Statement": [ { "Action": [ - "ecs:CreateCluster", "ecs:DeregisterContainerInstance", - "ecs:DiscoverPollEndpoint", - "ecs:Poll", "ecs:RegisterContainerInstance", - "ecs:StartTelemetrySession", - "ecs:Submit*", + "ecs:Submit*" + ], + "Effect": "Allow", + "Resource": { + "Fn::GetAtt": [ + "EcsCluster97242B84", + "Arn" + ] + } + }, + { + "Action": [ + "ecs:Poll", + "ecs:StartTelemetrySession" + ], + "Effect": "Allow", + "Resource": "*", + "Condition": { + "ArnEquals": { + "ecs:cluster": { + "Fn::GetAtt": [ + "EcsCluster97242B84", + "Arn" + ] + } + } + } + }, + { + "Action": [ + "ecs:DiscoverPollEndpoint", "ecr:GetAuthorizationToken", "logs:CreateLogStream", "logs:PutLogEvents" @@ -613,7 +639,17 @@ "ecs:DescribeTasks" ], "Effect": "Allow", - "Resource": "*" + "Resource": "*", + "Condition": { + "ArnEquals": { + "ecs:cluster": { + "Fn::GetAtt": [ + "EcsCluster97242B84", + "Arn" + ] + } + } + } }, { "Action": [ @@ -866,13 +902,39 @@ "Statement": [ { "Action": [ - "ecs:CreateCluster", "ecs:DeregisterContainerInstance", - "ecs:DiscoverPollEndpoint", - "ecs:Poll", "ecs:RegisterContainerInstance", - "ecs:StartTelemetrySession", - "ecs:Submit*", + "ecs:Submit*" + ], + "Effect": "Allow", + "Resource": { + "Fn::GetAtt": [ + "EcsCluster97242B84", + "Arn" + ] + } + }, + { + "Action": [ + "ecs:Poll", + "ecs:StartTelemetrySession" + ], + "Effect": "Allow", + "Resource": "*", + "Condition": { + "ArnEquals": { + "ecs:cluster": { + "Fn::GetAtt": [ + "EcsCluster97242B84", + "Arn" + ] + } + } + } + }, + { + "Action": [ + "ecs:DiscoverPollEndpoint", "ecr:GetAuthorizationToken", "logs:CreateLogStream", "logs:PutLogEvents" @@ -1058,7 +1120,17 @@ "ecs:DescribeTasks" ], "Effect": "Allow", - "Resource": "*" + "Resource": "*", + "Condition": { + "ArnEquals": { + "ecs:cluster": { + "Fn::GetAtt": [ + "EcsCluster97242B84", + "Arn" + ] + } + } + } }, { "Action": [ diff --git a/packages/@aws-cdk/aws-ecs/test/test.ecs-cluster.ts b/packages/@aws-cdk/aws-ecs/test/test.ecs-cluster.ts index 02d2594763da5..9eb4ca0ed3bc0 100644 --- a/packages/@aws-cdk/aws-ecs/test/test.ecs-cluster.ts +++ b/packages/@aws-cdk/aws-ecs/test/test.ecs-cluster.ts @@ -127,13 +127,39 @@ export = { Statement: [ { Action: [ - 'ecs:CreateCluster', 'ecs:DeregisterContainerInstance', - 'ecs:DiscoverPollEndpoint', - 'ecs:Poll', 'ecs:RegisterContainerInstance', - 'ecs:StartTelemetrySession', 'ecs:Submit*', + ], + Effect: 'Allow', + Resource: { + 'Fn::GetAtt': [ + 'EcsCluster97242B84', + 'Arn', + ], + }, + }, + { + Action: [ + 'ecs:Poll', + 'ecs:StartTelemetrySession', + ], + Effect: 'Allow', + Resource: '*', + Condition: { + ArnEquals: { + 'ecs:cluster': { + 'Fn::GetAtt': [ + 'EcsCluster97242B84', + 'Arn', + ], + }, + }, + }, + }, + { + Action: [ + 'ecs:DiscoverPollEndpoint', 'ecr:GetAuthorizationToken', 'logs:CreateLogStream', 'logs:PutLogEvents', @@ -272,13 +298,39 @@ export = { Statement: [ { Action: [ - 'ecs:CreateCluster', 'ecs:DeregisterContainerInstance', - 'ecs:DiscoverPollEndpoint', - 'ecs:Poll', 'ecs:RegisterContainerInstance', - 'ecs:StartTelemetrySession', 'ecs:Submit*', + ], + Effect: 'Allow', + Resource: { + 'Fn::GetAtt': [ + 'EcsCluster97242B84', + 'Arn', + ], + }, + }, + { + Action: [ + 'ecs:Poll', + 'ecs:StartTelemetrySession', + ], + Effect: 'Allow', + Resource: '*', + Condition: { + ArnEquals: { + 'ecs:cluster': { + 'Fn::GetAtt': [ + 'EcsCluster97242B84', + 'Arn', + ], + }, + }, + }, + }, + { + Action: [ + 'ecs:DiscoverPollEndpoint', 'ecr:GetAuthorizationToken', 'logs:CreateLogStream', 'logs:PutLogEvents', @@ -392,6 +444,16 @@ export = { ], Effect: 'Allow', Resource: '*', + Condition: { + ArnEquals: { + 'ecs:cluster': { + 'Fn::GetAtt': [ + 'EcsCluster97242B84', + 'Arn', + ], + }, + }, + }, }, { Action: [ @@ -572,13 +634,39 @@ export = { Statement: [ { Action: [ - 'ecs:CreateCluster', 'ecs:DeregisterContainerInstance', - 'ecs:DiscoverPollEndpoint', - 'ecs:Poll', 'ecs:RegisterContainerInstance', - 'ecs:StartTelemetrySession', 'ecs:Submit*', + ], + Effect: 'Allow', + Resource: { + 'Fn::GetAtt': [ + 'EcsCluster97242B84', + 'Arn', + ], + }, + }, + { + Action: [ + 'ecs:Poll', + 'ecs:StartTelemetrySession', + ], + Effect: 'Allow', + Resource: '*', + Condition: { + ArnEquals: { + 'ecs:cluster': { + 'Fn::GetAtt': [ + 'EcsCluster97242B84', + 'Arn', + ], + }, + }, + }, + }, + { + Action: [ + 'ecs:DiscoverPollEndpoint', 'ecr:GetAuthorizationToken', 'logs:CreateLogStream', 'logs:PutLogEvents', diff --git a/packages/@aws-cdk/aws-events-targets/test/ecs/integ.event-ec2-task.lit.expected.json b/packages/@aws-cdk/aws-events-targets/test/ecs/integ.event-ec2-task.lit.expected.json index d6af0f21c794a..5fb1bb925e0bf 100644 --- a/packages/@aws-cdk/aws-events-targets/test/ecs/integ.event-ec2-task.lit.expected.json +++ b/packages/@aws-cdk/aws-events-targets/test/ecs/integ.event-ec2-task.lit.expected.json @@ -261,13 +261,39 @@ "Statement": [ { "Action": [ - "ecs:CreateCluster", "ecs:DeregisterContainerInstance", - "ecs:DiscoverPollEndpoint", - "ecs:Poll", "ecs:RegisterContainerInstance", - "ecs:StartTelemetrySession", - "ecs:Submit*", + "ecs:Submit*" + ], + "Effect": "Allow", + "Resource": { + "Fn::GetAtt": [ + "EcsCluster97242B84", + "Arn" + ] + } + }, + { + "Action": [ + "ecs:Poll", + "ecs:StartTelemetrySession" + ], + "Effect": "Allow", + "Resource": "*", + "Condition": { + "ArnEquals": { + "ecs:cluster": { + "Fn::GetAtt": [ + "EcsCluster97242B84", + "Arn" + ] + } + } + } + }, + { + "Action": [ + "ecs:DiscoverPollEndpoint", "ecr:GetAuthorizationToken", "logs:CreateLogStream", "logs:PutLogEvents" @@ -449,7 +475,17 @@ "ecs:DescribeTasks" ], "Effect": "Allow", - "Resource": "*" + "Resource": "*", + "Condition": { + "ArnEquals": { + "ecs:cluster": { + "Fn::GetAtt": [ + "EcsCluster97242B84", + "Arn" + ] + } + } + } }, { "Action": [ diff --git a/packages/@aws-cdk/aws-lambda/README.md b/packages/@aws-cdk/aws-lambda/README.md index a08b04b31bb8c..07cb929bae067 100644 --- a/packages/@aws-cdk/aws-lambda/README.md +++ b/packages/@aws-cdk/aws-lambda/README.md @@ -362,7 +362,7 @@ new lambda.Function(this, 'Function', { command: [ 'bash', '-c', ` pip install -r requirements.txt -t /asset-output && - rsync -r . /asset-output + cp -au . /asset-output `, ], }, diff --git a/packages/@aws-cdk/aws-lambda/test/integ.bundling.ts b/packages/@aws-cdk/aws-lambda/test/integ.bundling.ts index 200ca72f204b6..c59d1eb85dcc9 100644 --- a/packages/@aws-cdk/aws-lambda/test/integ.bundling.ts +++ b/packages/@aws-cdk/aws-lambda/test/integ.bundling.ts @@ -20,7 +20,7 @@ class TestStack extends Stack { image: lambda.Runtime.PYTHON_3_6.bundlingDockerImage, command: [ 'bash', '-c', [ - 'rsync -r . /asset-output', + 'cp -au . /asset-output', 'cd /asset-output', 'pip install -r requirements.txt -t .', ].join(' && '), diff --git a/packages/@aws-cdk/aws-stepfunctions-tasks/test/ecs/integ.ec2-run-task.expected.json b/packages/@aws-cdk/aws-stepfunctions-tasks/test/ecs/integ.ec2-run-task.expected.json index 634506837f8d5..bf007c4e55c32 100644 --- a/packages/@aws-cdk/aws-stepfunctions-tasks/test/ecs/integ.ec2-run-task.expected.json +++ b/packages/@aws-cdk/aws-stepfunctions-tasks/test/ecs/integ.ec2-run-task.expected.json @@ -63,13 +63,39 @@ "Statement": [ { "Action": [ - "ecs:CreateCluster", "ecs:DeregisterContainerInstance", - "ecs:DiscoverPollEndpoint", - "ecs:Poll", "ecs:RegisterContainerInstance", - "ecs:StartTelemetrySession", - "ecs:Submit*", + "ecs:Submit*" + ], + "Effect": "Allow", + "Resource": { + "Fn::GetAtt": [ + "Ec2ClusterEE43E89D", + "Arn" + ] + } + }, + { + "Action": [ + "ecs:Poll", + "ecs:StartTelemetrySession" + ], + "Effect": "Allow", + "Resource": "*", + "Condition": { + "ArnEquals": { + "ecs:cluster": { + "Fn::GetAtt": [ + "Ec2ClusterEE43E89D", + "Arn" + ] + } + } + } + }, + { + "Action": [ + "ecs:DiscoverPollEndpoint", "ecr:GetAuthorizationToken", "logs:CreateLogStream", "logs:PutLogEvents" @@ -243,7 +269,17 @@ "ecs:DescribeTasks" ], "Effect": "Allow", - "Resource": "*" + "Resource": "*", + "Condition": { + "ArnEquals": { + "ecs:cluster": { + "Fn::GetAtt": [ + "Ec2ClusterEE43E89D", + "Arn" + ] + } + } + } }, { "Action": [ diff --git a/packages/@aws-cdk/aws-stepfunctions-tasks/test/ecs/integ.ec2-task.expected.json b/packages/@aws-cdk/aws-stepfunctions-tasks/test/ecs/integ.ec2-task.expected.json index 0a57d22dc1fb6..09e4369720880 100644 --- a/packages/@aws-cdk/aws-stepfunctions-tasks/test/ecs/integ.ec2-task.expected.json +++ b/packages/@aws-cdk/aws-stepfunctions-tasks/test/ecs/integ.ec2-task.expected.json @@ -63,13 +63,39 @@ "Statement": [ { "Action": [ - "ecs:CreateCluster", "ecs:DeregisterContainerInstance", - "ecs:DiscoverPollEndpoint", - "ecs:Poll", "ecs:RegisterContainerInstance", - "ecs:StartTelemetrySession", - "ecs:Submit*", + "ecs:Submit*" + ], + "Effect": "Allow", + "Resource": { + "Fn::GetAtt": [ + "FargateCluster7CCD5F93", + "Arn" + ] + } + }, + { + "Action": [ + "ecs:Poll", + "ecs:StartTelemetrySession" + ], + "Effect": "Allow", + "Resource": "*", + "Condition": { + "ArnEquals": { + "ecs:cluster": { + "Fn::GetAtt": [ + "FargateCluster7CCD5F93", + "Arn" + ] + } + } + } + }, + { + "Action": [ + "ecs:DiscoverPollEndpoint", "ecr:GetAuthorizationToken", "logs:CreateLogStream", "logs:PutLogEvents" @@ -243,7 +269,17 @@ "ecs:DescribeTasks" ], "Effect": "Allow", - "Resource": "*" + "Resource": "*", + "Condition": { + "ArnEquals": { + "ecs:cluster": { + "Fn::GetAtt": [ + "FargateCluster7CCD5F93", + "Arn" + ] + } + } + } }, { "Action": [ diff --git a/packages/@aws-cdk/cloudformation-include/lib/cfn-include.ts b/packages/@aws-cdk/cloudformation-include/lib/cfn-include.ts index a5f1c23d0a857..157a0ebaf6ccf 100644 --- a/packages/@aws-cdk/cloudformation-include/lib/cfn-include.ts +++ b/packages/@aws-cdk/cloudformation-include/lib/cfn-include.ts @@ -225,6 +225,7 @@ export class CfnInclude extends core.CfnElement { type: expression.Type, default: expression.Default, allowedPattern: expression.AllowedPattern, + allowedValues: expression.AllowedValues, constraintDescription: expression.ConstraintDescription, description: expression.Description, maxLength: expression.MaxLength, diff --git a/packages/@aws-cdk/cloudformation-include/test/test-templates/bucket-with-parameters.json b/packages/@aws-cdk/cloudformation-include/test/test-templates/bucket-with-parameters.json index deec4ffa24e81..0cf4552cb2951 100644 --- a/packages/@aws-cdk/cloudformation-include/test/test-templates/bucket-with-parameters.json +++ b/packages/@aws-cdk/cloudformation-include/test/test-templates/bucket-with-parameters.json @@ -15,6 +15,7 @@ "Description": "the time in seconds that a browser will cache the preflight response", "MaxValue": "300", "MinValue": "0", + "AllowedValues": [1, 2, 3, 10, 100, 300], "Type": "Number", "NoEcho": "true" } diff --git a/packages/@aws-cdk/core/lib/stack.ts b/packages/@aws-cdk/core/lib/stack.ts index 953587a40dec3..f913377b54a20 100644 --- a/packages/@aws-cdk/core/lib/stack.ts +++ b/packages/@aws-cdk/core/lib/stack.ts @@ -966,16 +966,8 @@ function mergeSection(section: string, val1: any, val2: any): any { return val1 ?? val2; case 'Transform': return mergeSets(val1, val2); - case 'Resources': - case 'Conditions': - case 'Parameters': - case 'Outputs': - case 'Mappings': - case 'Metadata': - return mergeObjectsWithoutDuplicates(section, val1, val2); default: - throw new Error(`CDK doesn't know how to merge two instances of the CFN template section '${section}' - ` + - 'please remove one of them from your code'); + return mergeObjectsWithoutDuplicates(section, val1, val2); } } diff --git a/packages/@aws-cdk/core/test/test.rule.ts b/packages/@aws-cdk/core/test/test.rule.ts index 34a3915daf152..8fa7ae9920fc7 100644 --- a/packages/@aws-cdk/core/test/test.rule.ts +++ b/packages/@aws-cdk/core/test/test.rule.ts @@ -29,4 +29,20 @@ export = { test.done(); }, + + 'a template can contain multiple Rules'(test: Test) { + const stack = new Stack(); + + new CfnRule(stack, 'Rule1'); + new CfnRule(stack, 'Rule2'); + + test.deepEqual(toCloudFormation(stack), { + Rules: { + Rule1: {}, + Rule2: {}, + }, + }); + + test.done(); + }, };