Skip to content

Commit 76a5cc7

Browse files
clareliguoririx0rrr
authored andcommitted
feat(aws-ecs): Support HTTPS in load balanced Fargate service (#1115)
1 parent fba780f commit 76a5cc7

File tree

3 files changed

+113
-2
lines changed

3 files changed

+113
-2
lines changed

packages/@aws-cdk/aws-ecs/lib/load-balanced-fargate-service.ts

+40-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1+
import { CertificateRef } from '@aws-cdk/aws-certificatemanager';
12
import elbv2 = require('@aws-cdk/aws-elasticloadbalancingv2');
3+
import { AliasRecord, HostedZoneRef } from '@aws-cdk/aws-route53';
24
import cdk = require('@aws-cdk/cdk');
35
import { ICluster } from './cluster';
46
import { IContainerImage } from './container-image';
@@ -83,6 +85,22 @@ export interface LoadBalancedFargateServiceProps {
8385
* @default 1
8486
*/
8587
desiredCount?: number;
88+
89+
/*
90+
* Domain name for the service, e.g. api.example.com
91+
*/
92+
domainName?: string;
93+
94+
/**
95+
* Route53 hosted zone for the domain, e.g. "example.com."
96+
*/
97+
domainZone?: HostedZoneRef;
98+
99+
/**
100+
* Certificate Manager certificate to associate with the load balancer.
101+
* Setting this option will set the load balancer port to 443.
102+
*/
103+
certificate?: CertificateRef;
86104
}
87105

88106
/**
@@ -123,12 +141,33 @@ export class LoadBalancedFargateService extends cdk.Construct {
123141

124142
this.loadBalancer = lb;
125143

126-
const listener = lb.addListener('PublicListener', { port: 80, open: true });
144+
let listener;
145+
if (typeof props.certificate !== 'undefined') {
146+
listener = lb.addListener('PublicListener', {
147+
port: 443,
148+
open: true,
149+
certificateArns: [props.certificate.certificateArn]
150+
});
151+
} else {
152+
listener = lb.addListener('PublicListener', { port: 80, open: true });
153+
}
154+
127155
listener.addTargets('ECS', {
128156
port: 80,
129157
targets: [service]
130158
});
131159

132160
new cdk.Output(this, 'LoadBalancerDNS', { value: lb.dnsName });
161+
162+
if (typeof props.domainName !== 'undefined') {
163+
if (typeof props.domainZone === 'undefined') {
164+
throw new Error('A Route53 hosted domain zone name is required to configure the specified domain name');
165+
}
166+
167+
new AliasRecord(props.domainZone, "DNS", {
168+
recordName: props.domainName,
169+
target: lb
170+
});
171+
}
133172
}
134173
}

packages/@aws-cdk/aws-ecs/package.json

+3-1
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@
6363
"dependencies": {
6464
"@aws-cdk/aws-applicationautoscaling": "^0.17.0",
6565
"@aws-cdk/aws-autoscaling": "^0.17.0",
66+
"@aws-cdk/aws-certificatemanager": "^0.17.0",
6667
"@aws-cdk/aws-cloudformation": "^0.17.0",
6768
"@aws-cdk/aws-cloudwatch": "^0.17.0",
6869
"@aws-cdk/aws-ec2": "^0.17.0",
@@ -72,6 +73,7 @@
7273
"@aws-cdk/aws-iam": "^0.17.0",
7374
"@aws-cdk/aws-lambda": "^0.17.0",
7475
"@aws-cdk/aws-logs": "^0.17.0",
76+
"@aws-cdk/aws-route53": "^0.17.0",
7577
"@aws-cdk/cdk": "^0.17.0",
7678
"@aws-cdk/cx-api": "^0.17.0"
7779
},
@@ -88,4 +90,4 @@
8890
"@aws-cdk/aws-logs": "^0.17.0",
8991
"@aws-cdk/cdk": "^0.17.0"
9092
}
91-
}
93+
}

packages/@aws-cdk/aws-ecs/test/test.l3s.ts

+70
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import { expect, haveResource } from '@aws-cdk/assert';
2+
import { CertificateRef } from '@aws-cdk/aws-certificatemanager';
23
import ec2 = require('@aws-cdk/aws-ec2');
4+
import { PublicHostedZone } from '@aws-cdk/aws-route53';
35
import cdk = require('@aws-cdk/cdk');
46
import { Test } from 'nodeunit';
57
import ecs = require('../lib');
@@ -52,6 +54,74 @@ export = {
5254
LaunchType: "FARGATE",
5355
}));
5456

57+
expect(stack).to(haveResource('AWS::ElasticLoadBalancingV2::Listener', {
58+
Port: 80
59+
}));
60+
61+
test.done();
62+
},
63+
64+
'test Fargateloadbalanced construct with TLS'(test: Test) {
65+
// GIVEN
66+
const stack = new cdk.Stack();
67+
const vpc = new ec2.VpcNetwork(stack, 'VPC');
68+
const cluster = new ecs.Cluster(stack, 'Cluster', { vpc });
69+
const zone = new PublicHostedZone(stack, 'HostedZone', { zoneName: 'example.com' });
70+
71+
// WHEN
72+
new ecs.LoadBalancedFargateService(stack, 'Service', {
73+
cluster,
74+
image: ecs.ContainerImage.fromDockerHub('test'),
75+
domainName: 'api.example.com',
76+
domainZone: zone,
77+
certificate: CertificateRef.import(stack, 'Cert', { certificateArn: 'helloworld' })
78+
});
79+
80+
// THEN - stack contains a load balancer and a service
81+
expect(stack).to(haveResource('AWS::ElasticLoadBalancingV2::LoadBalancer'));
82+
83+
expect(stack).to(haveResource('AWS::ElasticLoadBalancingV2::Listener', {
84+
Port: 443,
85+
Certificates: [{
86+
CertificateArn: "helloworld"
87+
}]
88+
}));
89+
90+
expect(stack).to(haveResource("AWS::ECS::Service", {
91+
DesiredCount: 1,
92+
LaunchType: "FARGATE",
93+
}));
94+
95+
expect(stack).to(haveResource('AWS::Route53::RecordSet', {
96+
Name: 'api.example.com.',
97+
HostedZoneId: {
98+
Ref: "HostedZoneDB99F866"
99+
},
100+
Type: 'A',
101+
AliasTarget: {
102+
HostedZoneId: { 'Fn::GetAtt': [ 'ServiceLBE9A1ADBC', 'CanonicalHostedZoneID' ] },
103+
DNSName: { 'Fn::GetAtt': [ 'ServiceLBE9A1ADBC', 'DNSName' ] },
104+
}
105+
}));
106+
107+
test.done();
108+
},
109+
110+
"errors when setting domainName but not domainZone"(test: Test) {
111+
// GIVEN
112+
const stack = new cdk.Stack();
113+
const vpc = new ec2.VpcNetwork(stack, 'VPC');
114+
const cluster = new ecs.Cluster(stack, 'Cluster', { vpc });
115+
116+
// THEN
117+
test.throws(() => {
118+
new ecs.LoadBalancedFargateService(stack, 'Service', {
119+
cluster,
120+
image: ecs.ContainerImage.fromDockerHub('test'),
121+
domainName: 'api.example.com'
122+
});
123+
});
124+
55125
test.done();
56126
},
57127

0 commit comments

Comments
 (0)