Skip to content

Commit

Permalink
feat(aws-ecs): Support HTTPS in load balanced Fargate service
Browse files Browse the repository at this point in the history
  • Loading branch information
clareliguori committed Nov 8, 2018
1 parent e404316 commit 4ff33c6
Show file tree
Hide file tree
Showing 3 changed files with 118 additions and 1 deletion.
49 changes: 48 additions & 1 deletion packages/@aws-cdk/aws-ecs/lib/load-balanced-fargate-service.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import elbv2 = require('@aws-cdk/aws-elasticloadbalancingv2');
import route53 = require('@aws-cdk/aws-route53');
import cdk = require('@aws-cdk/cdk');
import { ICluster } from './cluster';
import { IContainerImage } from './container-image';
Expand Down Expand Up @@ -76,6 +77,22 @@ export interface LoadBalancedFargateServiceProps {
* @default false
*/
publicTasks?: boolean;

/**
* Domain name for the service, e.g. api.example.com
*/
domainName?: string;

/**
* Route53 hosted zone name for the domain, e.g. "example.com."
*/
domainZone?: string;

/**
* ARN of the Certificate Manager SSL certificate to associate with the load balancer.
* Setting this option will set the load balancer port to 443.
*/
certificateArn?: string;
}

/**
Expand Down Expand Up @@ -115,12 +132,42 @@ export class LoadBalancedFargateService extends cdk.Construct {

this.loadBalancer = lb;

const listener = lb.addListener('PublicListener', { port: 80, open: true });
let listener;
if (props.certificateArn) {
// TODO once ALB redirects are supported in CloudFormation,
// redirect http to https
listener = lb.addListener('PublicListener', {
port: 443,
open: true,
certificateArns: [props.certificateArn]
});
} else {
listener = lb.addListener('PublicListener', { port: 80, open: true });
}

listener.addTargets('ECS', {
port: 80,
targets: [service]
});

new cdk.Output(this, 'LoadBalancerDNS', { value: lb.dnsName });

if (props.domainName) {
if (!props.domainZone) {
throw new Error('A Route53 hosted domain zone name is required to configure the specified domain name');
}

new route53.cloudformation.RecordSetGroupResource(this, "DNS", {
hostedZoneName: props.domainZone,
recordSets: [{
name: props.domainName,
type: 'A',
aliasTarget: {
hostedZoneId: lb.canonicalHostedZoneId,
dnsName: lb.dnsName
}
}]
});
}
}
}
1 change: 1 addition & 0 deletions packages/@aws-cdk/aws-ecs/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@
"@aws-cdk/aws-iam": "^0.15.1",
"@aws-cdk/aws-lambda": "^0.15.1",
"@aws-cdk/aws-logs": "^0.15.1",
"@aws-cdk/aws-route53": "^0.15.1",
"@aws-cdk/cdk": "^0.15.1",
"@aws-cdk/cx-api": "^0.15.1"
},
Expand Down
69 changes: 69 additions & 0 deletions packages/@aws-cdk/aws-ecs/test/test.l3s.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,75 @@ export = {
// THEN - stack containers a load balancer
expect(stack).to(haveResource('AWS::ElasticLoadBalancingV2::LoadBalancer'));

expect(stack).to(haveResource('AWS::ElasticLoadBalancingV2::Listener', {
Port: 80
}));

test.done();
},

'test Fargateloadbalanced construct with TLS'(test: Test) {
// GIVEN
const stack = new cdk.Stack();
const vpc = new ec2.VpcNetwork(stack, 'VPC');
const cluster = new ecs.Cluster(stack, 'Cluster', { vpc });

// WHEN
new ecs.LoadBalancedFargateService(stack, 'Service', {
cluster,
image: ecs.DockerHub.image('test'),
domainName: 'api.example.com',
domainZone: 'example.com.',
certificateArn: 'helloworld'
});

// THEN - stack contains a load balancer and a service
expect(stack).to(haveResource('AWS::ElasticLoadBalancingV2::LoadBalancer'));

expect(stack).to(haveResource('AWS::ElasticLoadBalancingV2::Listener', {
Port: 443,
Certificates: [{
CertificateArn: "helloworld"
}]
}));

expect(stack).to(haveResource("AWS::ECS::Service", {
DesiredCount: 1,
LaunchType: "FARGATE",
}));

expect(stack).to(haveResource('AWS::Route53::RecordSetGroup', {
HostedZoneName: "example.com.",
RecordSets: [
{
Name: 'api.example.com',
Type: 'A',
AliasTarget: {
HostedZoneId: { 'Fn::GetAtt': [ 'ServiceLBE9A1ADBC', 'CanonicalHostedZoneID' ] },
DNSName: { 'Fn::GetAtt': [ 'ServiceLBE9A1ADBC', 'DNSName' ] },
}
}
]
}));

test.done();
},

"errors when setting domainName but not domainZone"(test: Test) {
// GIVEN
const stack = new cdk.Stack();
const vpc = new ec2.VpcNetwork(stack, 'VPC');
const cluster = new ecs.Cluster(stack, 'Cluster', { vpc });

// THEN
test.throws(() => {
new ecs.LoadBalancedFargateService(stack, 'Service', {
cluster,
image: ecs.DockerHub.image('test'),
domainName: 'api.example.com'
});
});

test.done();
}
};

0 comments on commit 4ff33c6

Please sign in to comment.