diff --git a/packages/@aws-cdk/aws-codedeploy-api/.gitignore b/packages/@aws-cdk/aws-codedeploy-api/.gitignore new file mode 100644 index 0000000000000..7d1f7e309d0c3 --- /dev/null +++ b/packages/@aws-cdk/aws-codedeploy-api/.gitignore @@ -0,0 +1,15 @@ +*.js +*.js.map +*.d.ts +tsconfig.json +tslint.json +node_modules +*.generated.ts +dist +.jsii + +.LAST_BUILD +.nyc_output +coverage +.nycrc +.LAST_PACKAGE \ No newline at end of file diff --git a/packages/@aws-cdk/aws-codedeploy-api/.npmignore b/packages/@aws-cdk/aws-codedeploy-api/.npmignore new file mode 100644 index 0000000000000..e511c5acc268d --- /dev/null +++ b/packages/@aws-cdk/aws-codedeploy-api/.npmignore @@ -0,0 +1,14 @@ +# Don't include original .ts files when doing `npm pack` +*.ts +!*.d.ts +coverage +.nyc_output +*.tgz + +dist +.LAST_PACKAGE +.LAST_BUILD +!*.js + +# Include .jsii +!.jsii diff --git a/packages/@aws-cdk/aws-codedeploy-api/LICENSE b/packages/@aws-cdk/aws-codedeploy-api/LICENSE new file mode 100644 index 0000000000000..1739faaebb745 --- /dev/null +++ b/packages/@aws-cdk/aws-codedeploy-api/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2018-2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/packages/@aws-cdk/aws-codedeploy-api/NOTICE b/packages/@aws-cdk/aws-codedeploy-api/NOTICE new file mode 100644 index 0000000000000..95fd48569c743 --- /dev/null +++ b/packages/@aws-cdk/aws-codedeploy-api/NOTICE @@ -0,0 +1,2 @@ +AWS Cloud Development Kit (AWS CDK) +Copyright 2018-2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. diff --git a/packages/@aws-cdk/aws-codedeploy-api/README.md b/packages/@aws-cdk/aws-codedeploy-api/README.md new file mode 100644 index 0000000000000..231f860239c9a --- /dev/null +++ b/packages/@aws-cdk/aws-codedeploy-api/README.md @@ -0,0 +1,7 @@ +## AWS CodeDeploy Load Balancing API + +This package contains the abstract API of Load Balancers +as required by the AWS CodeDeploy Construct Library. + +You should never need to depend on it directly - +instead, depend on the `aws-codedeploy` package. diff --git a/packages/@aws-cdk/aws-codedeploy-api/lib/index.ts b/packages/@aws-cdk/aws-codedeploy-api/lib/index.ts new file mode 100644 index 0000000000000..c125cdad8aa0e --- /dev/null +++ b/packages/@aws-cdk/aws-codedeploy-api/lib/index.ts @@ -0,0 +1 @@ +export * from './load-balancer'; diff --git a/packages/@aws-cdk/aws-codedeploy-api/lib/load-balancer.ts b/packages/@aws-cdk/aws-codedeploy-api/lib/load-balancer.ts new file mode 100644 index 0000000000000..32b9218e438e1 --- /dev/null +++ b/packages/@aws-cdk/aws-codedeploy-api/lib/load-balancer.ts @@ -0,0 +1,32 @@ +/** + * The generations of AWS load balancing solutions. + */ +export enum LoadBalancerGeneration { + /** + * The first generation (ELB Classic). + */ + First, + + /** + * The second generation (ALB and NLB). + */ + Second +} + +/** + * The properties CodeDeploy requires of a load balancer. + */ +export interface ILoadBalancerProps { + generation: LoadBalancerGeneration; + name: string; +} + +/** + * An interface of an abstract laod balancer, as needed by CodeDeploy. + */ +export interface ILoadBalancer { + /** + * Specify the CodeDeploy-required properties of this load balancer. + */ + asCodeDeployLoadBalancer(): ILoadBalancerProps; +} diff --git a/packages/@aws-cdk/aws-codedeploy-api/package.json b/packages/@aws-cdk/aws-codedeploy-api/package.json new file mode 100644 index 0000000000000..ddc19c45e85bb --- /dev/null +++ b/packages/@aws-cdk/aws-codedeploy-api/package.json @@ -0,0 +1,61 @@ +{ + "name": "@aws-cdk/aws-codedeploy-api", + "version": "0.10.0", + "description": "Load Balancer API for AWS CodeDeploy", + "main": "lib/index.js", + "types": "lib/index.d.ts", + "jsii": { + "outdir": "dist", + "targets": { + "java": { + "package": "software.amazon.awscdk.services.codedeploy.api", + "maven": { + "groupId": "software.amazon.awscdk", + "artifactId": "codedeploy-api" + } + }, + "dotnet": { + "namespace": "Amazon.CDK.AWS.CodeDeploy.Api", + "packageId": "Amazon.CDK.AWS.CodeDeploy.Api", + "signAssembly": true, + "assemblyOriginatorKeyFile": "../../key.snk" + }, + "sphinx": {} + } + }, + "repository": { + "type": "git", + "url": "https://github.com/awslabs/aws-cdk.git" + }, + "scripts": { + "build": "cdk-build", + "watch": "cdk-watch", + "lint": "cdk-lint", + "test": "cdk-test", + "integ": "cdk-integ", + "pkglint": "pkglint -f", + "package": "cdk-package" + }, + "keywords": [ + "aws", + "cdk", + "constructs", + "codedeploy" + ], + "author": { + "name": "Amazon Web Services", + "url": "https://aws.amazon.com", + "organization": true + }, + "license": "Apache-2.0", + "devDependencies": { + "@aws-cdk/assert": "^0.10.0", + "cdk-build-tools": "^0.10.0", + "cfn2ts": "^0.10.0", + "pkglint": "^0.10.0" + }, + "dependencies": { + "@aws-cdk/cdk": "^0.10.0" + }, + "homepage": "https://github.com/awslabs/aws-cdk" +} diff --git a/packages/@aws-cdk/aws-codedeploy/README.md b/packages/@aws-cdk/aws-codedeploy/README.md index 601829edab23b..fbbb404a1af01 100644 --- a/packages/@aws-cdk/aws-codedeploy/README.md +++ b/packages/@aws-cdk/aws-codedeploy/README.md @@ -47,6 +47,50 @@ const deploymentGroup = codedeploy.ServerDeploymentGroupRef.import(this, 'Existi }); ``` +#### Load balancers + +You can [specify a load balancer](https://docs.aws.amazon.com/codedeploy/latest/userguide/integrations-aws-elastic-load-balancing.html) +with the `loadBalancer` property when creating a Deployment Group. + +With Classic Elastic Load Balancer, you provide it directly: + +```ts +import lb = require('@aws-cdk/aws-elasticloadbalancing'); + +const elb = new lb.LoadBalancer(this, 'ELB', { + // ... +}); +elb.addTarget(/* ... */); +elb.addListener({ + // ... +}); + +const deploymentGroup = new codedeploy.ServerDeploymentGroup(this, 'DeploymentGroup', { + loadBalancer: elb, +}); +``` + +With Application Load Balancer or Network Load Balancer, +you provide a Target Group as the load balancer: + +```ts +import lbv2 = require('@aws-cdk/aws-elasticloadbalancingv2'); + +const alb = new lbv2.ApplicationLoadBalancer(this, 'ALB', { + // ... +}); +const listener = alb.addListener('Listener', { + // ... +}); +const targetGroup = listener.addTargets('Fleet', { + // ... +}); + +const deploymentGroup = new codedeploy.ServerDeploymentGroup(this, 'DeploymentGroup', { + loadBalancer: targetGroup, +}); +``` + ### Deployment Configurations You can also pass a Deployment Configuration when creating the Deployment Group: diff --git a/packages/@aws-cdk/aws-codedeploy/lib/deployment-group.ts b/packages/@aws-cdk/aws-codedeploy/lib/deployment-group.ts index 4d488197d80ff..37f448669185d 100644 --- a/packages/@aws-cdk/aws-codedeploy/lib/deployment-group.ts +++ b/packages/@aws-cdk/aws-codedeploy/lib/deployment-group.ts @@ -1,4 +1,5 @@ import autoscaling = require("@aws-cdk/aws-autoscaling"); +import codedeploylb = require("@aws-cdk/aws-codedeploy-api"); import ec2 = require("@aws-cdk/aws-ec2"); import s3 = require("@aws-cdk/aws-s3"); import cdk = require("@aws-cdk/cdk"); @@ -143,6 +144,15 @@ export interface ServerDeploymentGroupProps { * @see https://docs.aws.amazon.com/codedeploy/latest/userguide/codedeploy-agent-operations-install.html */ installAgent?: boolean; + + /** + * The load balancer to place in front of this Deployment Group. + * Can be either a classic Elastic Load Balancer, + * or an Application Load Balancer / Network Load Balancer Target Group. + * + * @default the Deployment Group will not have a load balancer defined + */ + loadBalancer?: codedeploylb.ILoadBalancer; } /** @@ -188,6 +198,12 @@ export class ServerDeploymentGroup extends ServerDeploymentGroupRef { this._autoScalingGroups.length === 0 ? undefined : this._autoScalingGroups.map(asg => asg.autoScalingGroupName())), + loadBalancerInfo: this.loadBalancerInfo(props.loadBalancer), + deploymentStyle: props.loadBalancer === undefined + ? undefined + : { + deploymentOption: 'WITH_TRAFFIC_CONTROL', + }, }); this.deploymentGroupName = resource.deploymentGroupName; @@ -244,6 +260,30 @@ export class ServerDeploymentGroup extends ServerDeploymentGroupRef { break; } } + + private loadBalancerInfo(lbProvider?: codedeploylb.ILoadBalancer): + cloudformation.DeploymentGroupResource.LoadBalancerInfoProperty | undefined { + if (!lbProvider) { + return undefined; + } + + const lb = lbProvider.asCodeDeployLoadBalancer(); + + switch (lb.generation) { + case codedeploylb.LoadBalancerGeneration.First: + return { + elbInfoList: [ + { name: lb.name }, + ], + }; + case codedeploylb.LoadBalancerGeneration.Second: + return { + targetGroupInfoList: [ + { name: lb.name }, + ] + }; + } + } } function deploymentGroupName2Arn(applicationName: string, deploymentGroupName: string): string { diff --git a/packages/@aws-cdk/aws-codedeploy/package.json b/packages/@aws-cdk/aws-codedeploy/package.json index 30a51df38a081..832349d9290a6 100644 --- a/packages/@aws-cdk/aws-codedeploy/package.json +++ b/packages/@aws-cdk/aws-codedeploy/package.json @@ -54,12 +54,16 @@ "devDependencies": { "@aws-cdk/assert": "^0.10.0", "@aws-cdk/aws-ec2": "^0.10.0", + "@aws-cdk/aws-elasticloadbalancing": "^0.10.0", + "@aws-cdk/aws-elasticloadbalancingv2": "^0.10.0", "cdk-build-tools": "^0.10.0", + "cdk-integ-tools": "^0.10.0", "cfn2ts": "^0.10.0", "pkglint": "^0.10.0" }, "dependencies": { "@aws-cdk/aws-autoscaling": "^0.10.0", + "@aws-cdk/aws-codedeploy-api": "^0.10.0", "@aws-cdk/aws-codepipeline-api": "^0.10.0", "@aws-cdk/aws-s3": "^0.10.0", "@aws-cdk/cdk": "^0.10.0" diff --git a/packages/@aws-cdk/aws-codedeploy/test/integ.deployment-group.expected.json b/packages/@aws-cdk/aws-codedeploy/test/integ.deployment-group.expected.json new file mode 100644 index 0000000000000..d54b1bd9e06b5 --- /dev/null +++ b/packages/@aws-cdk/aws-codedeploy/test/integ.deployment-group.expected.json @@ -0,0 +1,775 @@ +{ + "Resources": { + "VPCB9E5F0B4": { + "Type": "AWS::EC2::VPC", + "Properties": { + "CidrBlock": "10.0.0.0/16", + "EnableDnsHostnames": true, + "EnableDnsSupport": true, + "InstanceTenancy": "default", + "Tags": [ + { + "Key": "Name", + "Value": "aws-cdk-codedeploy-server-dg/VPC" + } + ] + } + }, + "VPCPublicSubnet1SubnetB4246D30": { + "Type": "AWS::EC2::Subnet", + "Properties": { + "CidrBlock": "10.0.0.0/19", + "VpcId": { + "Ref": "VPCB9E5F0B4" + }, + "AvailabilityZone": "test-region-1a", + "MapPublicIpOnLaunch": true, + "Tags": [ + { + "Key": "Name", + "Value": "aws-cdk-codedeploy-server-dg/VPC/PublicSubnet1" + } + ] + } + }, + "VPCPublicSubnet1RouteTableFEE4B781": { + "Type": "AWS::EC2::RouteTable", + "Properties": { + "VpcId": { + "Ref": "VPCB9E5F0B4" + }, + "Tags": [ + { + "Key": "Name", + "Value": "aws-cdk-codedeploy-server-dg/VPC/PublicSubnet1" + } + ] + } + }, + "VPCPublicSubnet1RouteTableAssociatioin249B4093": { + "Type": "AWS::EC2::SubnetRouteTableAssociation", + "Properties": { + "RouteTableId": { + "Ref": "VPCPublicSubnet1RouteTableFEE4B781" + }, + "SubnetId": { + "Ref": "VPCPublicSubnet1SubnetB4246D30" + } + } + }, + "VPCPublicSubnet1EIP6AD938E8": { + "Type": "AWS::EC2::EIP", + "Properties": { + "Domain": "vpc" + } + }, + "VPCPublicSubnet1NATGatewayE0556630": { + "Type": "AWS::EC2::NatGateway", + "Properties": { + "AllocationId": { + "Fn::GetAtt": [ + "VPCPublicSubnet1EIP6AD938E8", + "AllocationId" + ] + }, + "SubnetId": { + "Ref": "VPCPublicSubnet1SubnetB4246D30" + }, + "Tags": [ + { + "Key": "Name", + "Value": "aws-cdk-codedeploy-server-dg/VPC/PublicSubnet1" + } + ] + } + }, + "VPCPublicSubnet1DefaultRoute91CEF279": { + "Type": "AWS::EC2::Route", + "Properties": { + "RouteTableId": { + "Ref": "VPCPublicSubnet1RouteTableFEE4B781" + }, + "DestinationCidrBlock": "0.0.0.0/0", + "GatewayId": { + "Ref": "VPCIGWB7E252D3" + } + } + }, + "VPCPublicSubnet2Subnet74179F39": { + "Type": "AWS::EC2::Subnet", + "Properties": { + "CidrBlock": "10.0.32.0/19", + "VpcId": { + "Ref": "VPCB9E5F0B4" + }, + "AvailabilityZone": "test-region-1b", + "MapPublicIpOnLaunch": true, + "Tags": [ + { + "Key": "Name", + "Value": "aws-cdk-codedeploy-server-dg/VPC/PublicSubnet2" + } + ] + } + }, + "VPCPublicSubnet2RouteTable6F1A15F1": { + "Type": "AWS::EC2::RouteTable", + "Properties": { + "VpcId": { + "Ref": "VPCB9E5F0B4" + }, + "Tags": [ + { + "Key": "Name", + "Value": "aws-cdk-codedeploy-server-dg/VPC/PublicSubnet2" + } + ] + } + }, + "VPCPublicSubnet2RouteTableAssociatioin766225D7": { + "Type": "AWS::EC2::SubnetRouteTableAssociation", + "Properties": { + "RouteTableId": { + "Ref": "VPCPublicSubnet2RouteTable6F1A15F1" + }, + "SubnetId": { + "Ref": "VPCPublicSubnet2Subnet74179F39" + } + } + }, + "VPCPublicSubnet2EIP4947BC00": { + "Type": "AWS::EC2::EIP", + "Properties": { + "Domain": "vpc" + } + }, + "VPCPublicSubnet2NATGateway3C070193": { + "Type": "AWS::EC2::NatGateway", + "Properties": { + "AllocationId": { + "Fn::GetAtt": [ + "VPCPublicSubnet2EIP4947BC00", + "AllocationId" + ] + }, + "SubnetId": { + "Ref": "VPCPublicSubnet2Subnet74179F39" + }, + "Tags": [ + { + "Key": "Name", + "Value": "aws-cdk-codedeploy-server-dg/VPC/PublicSubnet2" + } + ] + } + }, + "VPCPublicSubnet2DefaultRouteB7481BBA": { + "Type": "AWS::EC2::Route", + "Properties": { + "RouteTableId": { + "Ref": "VPCPublicSubnet2RouteTable6F1A15F1" + }, + "DestinationCidrBlock": "0.0.0.0/0", + "GatewayId": { + "Ref": "VPCIGWB7E252D3" + } + } + }, + "VPCPublicSubnet3Subnet631C5E25": { + "Type": "AWS::EC2::Subnet", + "Properties": { + "CidrBlock": "10.0.64.0/19", + "VpcId": { + "Ref": "VPCB9E5F0B4" + }, + "AvailabilityZone": "test-region-1c", + "MapPublicIpOnLaunch": true, + "Tags": [ + { + "Key": "Name", + "Value": "aws-cdk-codedeploy-server-dg/VPC/PublicSubnet3" + } + ] + } + }, + "VPCPublicSubnet3RouteTable98AE0E14": { + "Type": "AWS::EC2::RouteTable", + "Properties": { + "VpcId": { + "Ref": "VPCB9E5F0B4" + }, + "Tags": [ + { + "Key": "Name", + "Value": "aws-cdk-codedeploy-server-dg/VPC/PublicSubnet3" + } + ] + } + }, + "VPCPublicSubnet3RouteTableAssociatioinF4E24B3B": { + "Type": "AWS::EC2::SubnetRouteTableAssociation", + "Properties": { + "RouteTableId": { + "Ref": "VPCPublicSubnet3RouteTable98AE0E14" + }, + "SubnetId": { + "Ref": "VPCPublicSubnet3Subnet631C5E25" + } + } + }, + "VPCPublicSubnet3EIPAD4BC883": { + "Type": "AWS::EC2::EIP", + "Properties": { + "Domain": "vpc" + } + }, + "VPCPublicSubnet3NATGatewayD3048F5C": { + "Type": "AWS::EC2::NatGateway", + "Properties": { + "AllocationId": { + "Fn::GetAtt": [ + "VPCPublicSubnet3EIPAD4BC883", + "AllocationId" + ] + }, + "SubnetId": { + "Ref": "VPCPublicSubnet3Subnet631C5E25" + }, + "Tags": [ + { + "Key": "Name", + "Value": "aws-cdk-codedeploy-server-dg/VPC/PublicSubnet3" + } + ] + } + }, + "VPCPublicSubnet3DefaultRouteA0D29D46": { + "Type": "AWS::EC2::Route", + "Properties": { + "RouteTableId": { + "Ref": "VPCPublicSubnet3RouteTable98AE0E14" + }, + "DestinationCidrBlock": "0.0.0.0/0", + "GatewayId": { + "Ref": "VPCIGWB7E252D3" + } + } + }, + "VPCPrivateSubnet1Subnet8BCA10E0": { + "Type": "AWS::EC2::Subnet", + "Properties": { + "CidrBlock": "10.0.96.0/19", + "VpcId": { + "Ref": "VPCB9E5F0B4" + }, + "AvailabilityZone": "test-region-1a", + "MapPublicIpOnLaunch": false, + "Tags": [ + { + "Key": "Name", + "Value": "aws-cdk-codedeploy-server-dg/VPC/PrivateSubnet1" + } + ] + } + }, + "VPCPrivateSubnet1RouteTableBE8A6027": { + "Type": "AWS::EC2::RouteTable", + "Properties": { + "VpcId": { + "Ref": "VPCB9E5F0B4" + }, + "Tags": [ + { + "Key": "Name", + "Value": "aws-cdk-codedeploy-server-dg/VPC/PrivateSubnet1" + } + ] + } + }, + "VPCPrivateSubnet1RouteTableAssociatioin77F7CA18": { + "Type": "AWS::EC2::SubnetRouteTableAssociation", + "Properties": { + "RouteTableId": { + "Ref": "VPCPrivateSubnet1RouteTableBE8A6027" + }, + "SubnetId": { + "Ref": "VPCPrivateSubnet1Subnet8BCA10E0" + } + } + }, + "VPCPrivateSubnet1DefaultRouteAE1D6490": { + "Type": "AWS::EC2::Route", + "Properties": { + "RouteTableId": { + "Ref": "VPCPrivateSubnet1RouteTableBE8A6027" + }, + "DestinationCidrBlock": "0.0.0.0/0", + "NatGatewayId": { + "Ref": "VPCPublicSubnet1NATGatewayE0556630" + } + } + }, + "VPCPrivateSubnet2SubnetCFCDAA7A": { + "Type": "AWS::EC2::Subnet", + "Properties": { + "CidrBlock": "10.0.128.0/19", + "VpcId": { + "Ref": "VPCB9E5F0B4" + }, + "AvailabilityZone": "test-region-1b", + "MapPublicIpOnLaunch": false, + "Tags": [ + { + "Key": "Name", + "Value": "aws-cdk-codedeploy-server-dg/VPC/PrivateSubnet2" + } + ] + } + }, + "VPCPrivateSubnet2RouteTable0A19E10E": { + "Type": "AWS::EC2::RouteTable", + "Properties": { + "VpcId": { + "Ref": "VPCB9E5F0B4" + }, + "Tags": [ + { + "Key": "Name", + "Value": "aws-cdk-codedeploy-server-dg/VPC/PrivateSubnet2" + } + ] + } + }, + "VPCPrivateSubnet2RouteTableAssociatioinC31995B4": { + "Type": "AWS::EC2::SubnetRouteTableAssociation", + "Properties": { + "RouteTableId": { + "Ref": "VPCPrivateSubnet2RouteTable0A19E10E" + }, + "SubnetId": { + "Ref": "VPCPrivateSubnet2SubnetCFCDAA7A" + } + } + }, + "VPCPrivateSubnet2DefaultRouteF4F5CFD2": { + "Type": "AWS::EC2::Route", + "Properties": { + "RouteTableId": { + "Ref": "VPCPrivateSubnet2RouteTable0A19E10E" + }, + "DestinationCidrBlock": "0.0.0.0/0", + "NatGatewayId": { + "Ref": "VPCPublicSubnet2NATGateway3C070193" + } + } + }, + "VPCPrivateSubnet3Subnet3EDCD457": { + "Type": "AWS::EC2::Subnet", + "Properties": { + "CidrBlock": "10.0.160.0/19", + "VpcId": { + "Ref": "VPCB9E5F0B4" + }, + "AvailabilityZone": "test-region-1c", + "MapPublicIpOnLaunch": false, + "Tags": [ + { + "Key": "Name", + "Value": "aws-cdk-codedeploy-server-dg/VPC/PrivateSubnet3" + } + ] + } + }, + "VPCPrivateSubnet3RouteTable192186F8": { + "Type": "AWS::EC2::RouteTable", + "Properties": { + "VpcId": { + "Ref": "VPCB9E5F0B4" + }, + "Tags": [ + { + "Key": "Name", + "Value": "aws-cdk-codedeploy-server-dg/VPC/PrivateSubnet3" + } + ] + } + }, + "VPCPrivateSubnet3RouteTableAssociatioin3B0B6B38": { + "Type": "AWS::EC2::SubnetRouteTableAssociation", + "Properties": { + "RouteTableId": { + "Ref": "VPCPrivateSubnet3RouteTable192186F8" + }, + "SubnetId": { + "Ref": "VPCPrivateSubnet3Subnet3EDCD457" + } + } + }, + "VPCPrivateSubnet3DefaultRoute27F311AE": { + "Type": "AWS::EC2::Route", + "Properties": { + "RouteTableId": { + "Ref": "VPCPrivateSubnet3RouteTable192186F8" + }, + "DestinationCidrBlock": "0.0.0.0/0", + "NatGatewayId": { + "Ref": "VPCPublicSubnet3NATGatewayD3048F5C" + } + } + }, + "VPCIGWB7E252D3": { + "Type": "AWS::EC2::InternetGateway", + "Properties": { + "Tags": [ + { + "Key": "Name", + "Value": "aws-cdk-codedeploy-server-dg/VPC" + } + ] + } + }, + "VPCVPCGW99B986DC": { + "Type": "AWS::EC2::VPCGatewayAttachment", + "Properties": { + "VpcId": { + "Ref": "VPCB9E5F0B4" + }, + "InternetGatewayId": { + "Ref": "VPCIGWB7E252D3" + } + } + }, + "ASGInstanceSecurityGroup0525485D": { + "Type": "AWS::EC2::SecurityGroup", + "Properties": { + "GroupDescription": "aws-cdk-codedeploy-server-dg/ASG/InstanceSecurityGroup", + "SecurityGroupEgress": [ + { + "CidrIp": "0.0.0.0/0", + "Description": "Outbound traffic allowed by default", + "FromPort": -1, + "IpProtocol": "-1", + "ToPort": -1 + } + ], + "SecurityGroupIngress": [], + "Tags": [ + { + "Key": "Name", + "Value": "aws-cdk-codedeploy-server-dg/ASG" + } + ], + "VpcId": { + "Ref": "VPCB9E5F0B4" + } + } + }, + "ASGInstanceRoleE263A41B": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "ec2.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + } + } + }, + "ASGInstanceRoleDefaultPolicy7636D8BF": { + "Type": "AWS::IAM::Policy", + "Properties": { + "PolicyDocument": { + "Statement": [ + { + "Action": [ + "s3:GetObject*", + "s3:GetBucket*", + "s3:List*" + ], + "Effect": "Allow", + "Resource": [ + { + "Fn::Join": [ + "", + [ + "arn", + ":", + { + "Ref": "AWS::Partition" + }, + ":", + "s3", + ":", + "", + ":", + "", + ":", + { + "Fn::Join": [ + "", + [ + "aws-codedeploy-", + { + "Ref": "AWS::Region" + } + ] + ] + } + ] + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::Join": [ + "", + [ + "arn", + ":", + { + "Ref": "AWS::Partition" + }, + ":", + "s3", + ":", + "", + ":", + "", + ":", + { + "Fn::Join": [ + "", + [ + "aws-codedeploy-", + { + "Ref": "AWS::Region" + } + ] + ] + } + ] + ] + }, + "/", + "latest/*" + ] + ] + } + ] + } + ], + "Version": "2012-10-17" + }, + "PolicyName": "ASGInstanceRoleDefaultPolicy7636D8BF", + "Roles": [ + { + "Ref": "ASGInstanceRoleE263A41B" + } + ] + } + }, + "ASGInstanceProfile0A2834D7": { + "Type": "AWS::IAM::InstanceProfile", + "Properties": { + "Roles": [ + { + "Ref": "ASGInstanceRoleE263A41B" + } + ] + } + }, + "ASGLaunchConfigC00AF12B": { + "Type": "AWS::AutoScaling::LaunchConfiguration", + "Properties": { + "ImageId": "ami-1234", + "InstanceType": "m5.large", + "IamInstanceProfile": { + "Ref": "ASGInstanceProfile0A2834D7" + }, + "SecurityGroups": [ + { + "Fn::GetAtt": [ + "ASGInstanceSecurityGroup0525485D", + "GroupId" + ] + } + ], + "UserData": { + "Fn::Base64": { + "Fn::Join": [ + "", + [ + "#!/bin/bash\nPKG_CMD=`which yum 2>/dev/null`\nif [ -z \"$PKG_CMD\" ]; then\nPKG_CMD=apt-get\nelse\nPKG=CMD=yum\nfi\n$PKG_CMD update -y\n$PKG_CMD install -y ruby2.0\nif [ $? -ne 0 ]; then\n$PKG_CMD install -y ruby\nfi\n$PKG_CMD install -y awscli\nTMP_DIR=`mktemp -d`\ncd $TMP_DIR\naws s3 cp s3://aws-codedeploy-", + { + "Ref": "AWS::Region" + }, + "/latest/install . --region ", + { + "Ref": "AWS::Region" + }, + "\nchmod +x ./install\n./install auto\nrm -fr $TMP_DIR" + ] + ] + } + } + }, + "DependsOn": [ + "ASGInstanceRoleE263A41B", + "ASGInstanceRoleDefaultPolicy7636D8BF" + ] + }, + "ASG46ED3070": { + "Type": "AWS::AutoScaling::AutoScalingGroup", + "Properties": { + "MaxSize": "1", + "MinSize": "1", + "DesiredCapacity": "1", + "LaunchConfigurationName": { + "Ref": "ASGLaunchConfigC00AF12B" + }, + "Tags": [ + { + "Key": "Name", + "PropagateAtLaunch": true, + "Value": "aws-cdk-codedeploy-server-dg/ASG" + } + ], + "VPCZoneIdentifier": [ + { + "Ref": "VPCPrivateSubnet1Subnet8BCA10E0" + }, + { + "Ref": "VPCPrivateSubnet2SubnetCFCDAA7A" + }, + { + "Ref": "VPCPrivateSubnet3Subnet3EDCD457" + } + ] + }, + "UpdatePolicy": { + "AutoScalingScheduledAction": { + "IgnoreUnmodifiedGroupSizeProperties": true + } + } + }, + "ELBSecurityGroupF148FD2E": { + "Type": "AWS::EC2::SecurityGroup", + "Properties": { + "GroupDescription": "aws-cdk-codedeploy-server-dg/ELB/SecurityGroup", + "SecurityGroupEgress": [], + "SecurityGroupIngress": [ + { + "CidrIp": "0.0.0.0/0", + "Description": "Default rule allow on 80", + "FromPort": 80, + "IpProtocol": "tcp", + "ToPort": 80 + } + ], + "Tags": [], + "VpcId": { + "Ref": "VPCB9E5F0B4" + } + } + }, + "ELBF276B85D": { + "Type": "AWS::ElasticLoadBalancing::LoadBalancer", + "Properties": { + "Listeners": [ + { + "InstancePort": "80", + "InstanceProtocol": "http", + "LoadBalancerPort": "80", + "Protocol": "http" + } + ], + "Scheme": "internal", + "SecurityGroups": [ + { + "Fn::GetAtt": [ + "ELBSecurityGroupF148FD2E", + "GroupId" + ] + } + ], + "Subnets": [ + { + "Ref": "VPCPrivateSubnet1Subnet8BCA10E0" + }, + { + "Ref": "VPCPrivateSubnet2SubnetCFCDAA7A" + }, + { + "Ref": "VPCPrivateSubnet3Subnet3EDCD457" + } + ] + } + }, + "CodeDeployGroupApplication13EFBDA6": { + "Type": "AWS::CodeDeploy::Application", + "Properties": { + "ComputePlatform": "Server" + } + }, + "CodeDeployGroupRole1D304F7A": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "codedeploy.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + }, + "ManagedPolicyArns": [ + "arn:aws:iam::aws:policy/service-role/AWSCodeDeployRole" + ] + } + }, + "CodeDeployGroup58220FC8": { + "Type": "AWS::CodeDeploy::DeploymentGroup", + "Properties": { + "ApplicationName": { + "Ref": "CodeDeployGroupApplication13EFBDA6" + }, + "ServiceRoleArn": { + "Fn::GetAtt": [ + "CodeDeployGroupRole1D304F7A", + "Arn" + ] + }, + "AutoScalingGroups": [ + { + "Ref": "ASG46ED3070" + } + ], + "DeploymentConfigName": "CodeDeployDefault.AllAtOnce", + "DeploymentStyle": { + "DeploymentOption": "WITH_TRAFFIC_CONTROL" + }, + "LoadBalancerInfo": { + "ElbInfoList": [ + { + "Name": { + "Ref": "ELBF276B85D" + } + } + ] + } + } + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-codedeploy/test/integ.deployment-group.ts b/packages/@aws-cdk/aws-codedeploy/test/integ.deployment-group.ts new file mode 100644 index 0000000000000..abc3c186e7f43 --- /dev/null +++ b/packages/@aws-cdk/aws-codedeploy/test/integ.deployment-group.ts @@ -0,0 +1,30 @@ +import autoscaling = require('@aws-cdk/aws-autoscaling'); +import ec2 = require('@aws-cdk/aws-ec2'); +import lb = require('@aws-cdk/aws-elasticloadbalancing'); +import cdk = require('@aws-cdk/cdk'); +import codedeploy = require('../lib'); + +const app = new cdk.App(process.argv); + +const stack = new cdk.Stack(app, 'aws-cdk-codedeploy-server-dg'); + +const vpc = new ec2.VpcNetwork(stack, 'VPC'); + +const asg = new autoscaling.AutoScalingGroup(stack, 'ASG', { + instanceType: new ec2.InstanceTypePair(ec2.InstanceClass.M5, ec2.InstanceSize.Large), + machineImage: new ec2.AmazonLinuxImage(), + vpc, +}); + +const elb = new lb.LoadBalancer(stack, 'ELB', { vpc }); +elb.addListener({ + externalPort: 80, +}); + +new codedeploy.ServerDeploymentGroup(stack, 'CodeDeployGroup', { + deploymentConfig: codedeploy.ServerDeploymentConfig.AllAtOnce, + autoScalingGroups: [asg], + loadBalancer: elb, +}); + +process.stdout.write(app.run()); diff --git a/packages/@aws-cdk/aws-codedeploy/test/test.deployment-group.ts b/packages/@aws-cdk/aws-codedeploy/test/test.deployment-group.ts index bd20dfb1bf707..48114508654a6 100644 --- a/packages/@aws-cdk/aws-codedeploy/test/test.deployment-group.ts +++ b/packages/@aws-cdk/aws-codedeploy/test/test.deployment-group.ts @@ -1,6 +1,7 @@ import { expect, haveResource } from '@aws-cdk/assert'; import autoscaling = require('@aws-cdk/aws-autoscaling'); import ec2 = require('@aws-cdk/aws-ec2'); +import lbv2 = require('@aws-cdk/aws-elasticloadbalancingv2'); import cdk = require('@aws-cdk/cdk'); import { Test } from 'nodeunit'; import codedeploy = require('../lib'); @@ -88,5 +89,73 @@ export = { test.done(); }, + + 'can be created with an ALB Target Group as the load balancer'(test: Test) { + const stack = new cdk.Stack(); + + const alb = new lbv2.ApplicationLoadBalancer(stack, 'ALB', { + vpc: new ec2.VpcNetwork(stack, 'VPC'), + }); + const listener = alb.addListener('Listener', { protocol: lbv2.ApplicationProtocol.Http }); + const targetGroup = listener.addTargets('Fleet', { protocol: lbv2.ApplicationProtocol.Http }); + + new codedeploy.ServerDeploymentGroup(stack, 'DeploymentGroup', { + loadBalancer: targetGroup, + }); + + expect(stack).to(haveResource('AWS::CodeDeploy::DeploymentGroup', { + "LoadBalancerInfo": { + "TargetGroupInfoList": [ + { + "Name": { + "Fn::GetAtt": [ + "ALBListenerFleetGroup008CEEE4", + "TargetGroupName", + ], + }, + }, + ], + }, + "DeploymentStyle": { + "DeploymentOption": "WITH_TRAFFIC_CONTROL", + }, + })); + + test.done(); + }, + + 'can be created with an NLB Target Group as the load balancer'(test: Test) { + const stack = new cdk.Stack(); + + const nlb = new lbv2.NetworkLoadBalancer(stack, 'NLB', { + vpc: new ec2.VpcNetwork(stack, 'VPC'), + }); + const listener = nlb.addListener('Listener', { port: 80 }); + const targetGroup = listener.addTargets('Fleet', { port: 80 }); + + new codedeploy.ServerDeploymentGroup(stack, 'DeploymentGroup', { + loadBalancer: targetGroup, + }); + + expect(stack).to(haveResource('AWS::CodeDeploy::DeploymentGroup', { + "LoadBalancerInfo": { + "TargetGroupInfoList": [ + { + "Name": { + "Fn::GetAtt": [ + "NLBListenerFleetGroupB882EC86", + "TargetGroupName", + ], + }, + }, + ], + }, + "DeploymentStyle": { + "DeploymentOption": "WITH_TRAFFIC_CONTROL", + }, + })); + + test.done(); + }, }, }; diff --git a/packages/@aws-cdk/aws-codepipeline-api/README.md b/packages/@aws-cdk/aws-codepipeline-api/README.md index 2f19570b6d1e0..bd7b88fd43aa9 100644 --- a/packages/@aws-cdk/aws-codepipeline-api/README.md +++ b/packages/@aws-cdk/aws-codepipeline-api/README.md @@ -4,7 +4,7 @@ This package contains the abstract API of Pipeline Actions. It's used by the `aws-codepipeline` module, and the AWS service modules that integrate with AWS CodePipeline. -You shoould never need to depend on it directly - +You should never need to depend on it directly - instead, depend on `aws-codepipeline`, and the module you need the concrete Actions from (like `aws-codecommit`, `aws-codebuild`, etc.). diff --git a/packages/@aws-cdk/aws-elasticloadbalancing/lib/load-balancer.ts b/packages/@aws-cdk/aws-elasticloadbalancing/lib/load-balancer.ts index 6136f90e37672..953a0cacaad61 100644 --- a/packages/@aws-cdk/aws-elasticloadbalancing/lib/load-balancer.ts +++ b/packages/@aws-cdk/aws-elasticloadbalancing/lib/load-balancer.ts @@ -1,3 +1,4 @@ +import codedeploy = require('@aws-cdk/aws-codedeploy-api'); import { AnyIPv4, Connections, IConnectable, IPortRange, SecurityGroup, SecurityGroupRef, TcpPort, VpcNetworkRef, VpcSubnetRef } from '@aws-cdk/aws-ec2'; import cdk = require('@aws-cdk/cdk'); @@ -185,7 +186,7 @@ export enum LoadBalancingProtocol { * * Routes to a fleet of of instances in a VPC. */ -export class LoadBalancer extends cdk.Construct implements IConnectable { +export class LoadBalancer extends cdk.Construct implements IConnectable, codedeploy.ILoadBalancer { /** * Control all connections from and to this load balancer */ @@ -286,6 +287,13 @@ export class LoadBalancer extends cdk.Construct implements IConnectable { return this.elb.loadBalancerSourceSecurityGroupOwnerAlias; } + public asCodeDeployLoadBalancer(): codedeploy.ILoadBalancerProps { + return { + generation: codedeploy.LoadBalancerGeneration.First, + name: this.loadBalancerName, + }; + } + /** * Allow connections to all existing targets on new instance port */ diff --git a/packages/@aws-cdk/aws-elasticloadbalancing/package.json b/packages/@aws-cdk/aws-elasticloadbalancing/package.json index 8e2aa78137bf6..9ce1c4c05b07a 100644 --- a/packages/@aws-cdk/aws-elasticloadbalancing/package.json +++ b/packages/@aws-cdk/aws-elasticloadbalancing/package.json @@ -59,6 +59,7 @@ "pkglint": "^0.10.0" }, "dependencies": { + "@aws-cdk/aws-codedeploy-api": "^0.10.0", "@aws-cdk/aws-ec2": "^0.10.0", "@aws-cdk/cdk": "^0.10.0" }, diff --git a/packages/@aws-cdk/aws-elasticloadbalancingv2/lib/shared/base-target-group.ts b/packages/@aws-cdk/aws-elasticloadbalancingv2/lib/shared/base-target-group.ts index 696ce8108d865..f78123a5536e9 100644 --- a/packages/@aws-cdk/aws-elasticloadbalancingv2/lib/shared/base-target-group.ts +++ b/packages/@aws-cdk/aws-elasticloadbalancingv2/lib/shared/base-target-group.ts @@ -1,3 +1,4 @@ +import codedeploy = require('@aws-cdk/aws-codedeploy-api'); import ec2 = require('@aws-cdk/aws-ec2'); import cdk = require('@aws-cdk/cdk'); import { cloudformation } from '../elasticloadbalancingv2.generated'; @@ -119,7 +120,7 @@ export interface HealthCheck { /** * Define the target of a load balancer */ -export abstract class BaseTargetGroup extends cdk.Construct implements ITargetGroup { +export abstract class BaseTargetGroup extends cdk.Construct implements ITargetGroup, codedeploy.ILoadBalancer { /** * The ARN of the target group */ @@ -234,6 +235,13 @@ export abstract class BaseTargetGroup extends cdk.Construct implements ITargetGr this.resource.addDependency(...other); } + public asCodeDeployLoadBalancer(): codedeploy.ILoadBalancerProps { + return { + generation: codedeploy.LoadBalancerGeneration.Second, + name: this.targetGroupName, + }; + } + /** * Register the given load balancing target as part of this group */ diff --git a/packages/@aws-cdk/aws-elasticloadbalancingv2/package.json b/packages/@aws-cdk/aws-elasticloadbalancingv2/package.json index 2c858af1873be..f1a3dee9db99b 100644 --- a/packages/@aws-cdk/aws-elasticloadbalancingv2/package.json +++ b/packages/@aws-cdk/aws-elasticloadbalancingv2/package.json @@ -59,6 +59,7 @@ "pkglint": "^0.10.0" }, "dependencies": { + "@aws-cdk/aws-codedeploy-api": "^0.10.0", "@aws-cdk/aws-ec2": "^0.10.0", "@aws-cdk/aws-s3": "^0.10.0", "@aws-cdk/cdk": "^0.10.0"