Thanks!
A verification email has been sent to
This article is part of a series:
Previously, we were able to deploy a simple Nestjs web server to ECS fargate and serve it through a load balancer. However, that connection is not secure and the url is not very user friendly so in this article we will go over serving the application with our own domain name and securing it with a SSL Certificate.
The starting point for this article is the repository for deploying application to ECS Fargate. You can find the finished code on github.
To follow along you will definitely need a domain name. Should you have a domain outside of aws you can checkout this article to create a hosted zone in AWS with your existing domain – moving domain over to AWS not required. Other than that, you will also need to generate a certificate for your domain in the AWS Certificate Manager in the same region as your ECS deployment.
To serve our application over https we're going to need a couple things. First we need to add a listener to our Load Balancer with https protocol and port 443, this will also require from us to provide a certificate. Then, we will need to open up the port 443 in Load Balancer's security group to allow traffic on that port.
Double click to copy1// lib/elastic-container.stack.ts2const CERTIFICATE_ARN =3 'arn:aws:acm:eu-central-1:123456789012:certificate/uuid';45albSg.addIngressRule(Peer.anyIpv4(), Port.tcp(443));6const sslListener = this.loadBalancer.addListener('secure https listener', {7 port: 443,8 open: true,9 sslPolicy: SslPolicy.RECOMMENDED,10 certificates: [{ certificateArn: CERTIFICATE_ARN }],11});1213const targetGroup = sslListener.addTargets('tcp-listener-target', {14 targetGroupName: 'tcp-target-ecs-service',15 protocol: ApplicationProtocol.HTTP,16 protocolVersion: ApplicationProtocolVersion.HTTP1,17});
Here, we have also assigned our target group to the sslListener
rather than httpListener
. Speaking of which, it would be good to redirect users to https even if they're using http.
Double click to copy1// lib/elastic-container.stack.ts2const httpListener = this.loadBalancer.addListener('http listener', {3 port: 80,4 open: true,5 defaultAction: ListenerAction.redirect({6 port: '443',7 protocol: ApplicationProtocol.HTTPS,8 }),9});
By changing the default behaviour, we can reroute all requests on port 80 to port 443 – HTTPS – then we will use our target group and route users to our app.
Last but not least, we will need access to the load balancer in the next step, so we need to assign it to a property on the stack instance.
Double click to copy1// lib/elastic-container.stack.ts2this.loadBalancer = new ApplicationLoadBalancer(...)
Now that we've assigned a certificate to the load balancer, we only have to create an appropriate DNS Record to be able to use the domain along with our SSL Certificate.
For this we will need to things: hosted zone id and hosted zone name. The hosted zone name is the domain name or subdomain you used to create your hosted zone, e.g., dev.exanubes.com
. Both hosted zone id and hosted zone name can be found in Route 53 > Hosted zones
Now, once you have this, we can create an instance of our existing hosted zone.
Double click to copy1// lib/route53.stack.ts2interface Props extends StackProps {3 loadBalancer: IApplicationLoadBalancer;4}5const HOSTED_ZONE_ID = 'YOUR_HOSTED_ZONE_ID';6export class Route53Stack extends Stack {7 constructor(scope: Construct, id: string, props: Props) {8 super(scope, id, props);9 const hostedZone = HostedZone.fromHostedZoneAttributes(10 this,11 'hosted-zone',12 {13 hostedZoneId: HOSTED_ZONE_ID,14 zoneName: 'dev.exanubes.com',15 }16 );17 }18}
And finally, we can create an Alias and use the Load Balancer as target.
Double click to copy1// lib/route53.stack.ts2new ARecord(this, 'ecs-alb-alias-record', {3 zone: hostedZone,4 target: RecordTarget.fromAlias(5 new targets.LoadBalancerTarget(props.loadBalancer)6 ),7});
Now it's time to create our stacks and deploy
Double click to copy1// bin/ecs-fargate-deployment.ts2const app = new cdk.App();3const ecr = new EcrStack(app, EcrStack.name, {});4const vpc = new VpcStack(app, VpcStack.name, {});5const ecs = new ElasticContainerStack(app, ElasticContainerStack.name, {6 vpc: vpc.vpc,7 repository: ecr.repository,8});9new Route53Stack(app, Route53Stack.name, { loadBalancer: ecs.loadBalancer });
Here, you can see why we needed to assign load balancer to a public property on the class instance as we are passing it to the Route53Stack
.
When deploying, keep in mind you will have to upload a Docker Image, otherwise it will hang when deploying the ElasticContainerStack
, you can do it while it's deploying all stacks or deploy EcrStack
first:
Double click to copynpm run build && npm run cdk:deploy EcrStack
and after pushing a Docker Image to ECR:
Double click to copynpm run cdk:deploy -- --all
Now you can go to the domain name you chose for the application and it should automatically redirect you to a secure connection – https.
Don't forget to tear it all down when you're done:
Double click to copynpm run cdk:destroy -- --all
To sum up, in this article we've successfully reached our application via our own domain name and secured it with an SSL Certificate.
However, this is not yet a modern application as each time we'd like to release a new version, we'd have to manually go through the release process. Next up, deploying Fargate application with a CI/CD Pipeline.
A verification email has been sent to