Where there’s a will there’s a way – Adding listeners to an AWS CDK ApplicationLoadBalancedFargateService

I recently needed to do a quick implementation of a third-party containerized tool for consumption by a development team who was using an old, unmaintained, and publicly hosted version.

We’ve recently been reducing our reliance on publicly hosted tools for simple functions (like testing HTTP connections, or determining the current IP address of a client) as in many cases we simply don’t know who’s maintaining the tool on the other end, and what they’re doing with that request data. It’s much better for us to host a little utility ourselves

For a quick and simple containerized tool implementation, I naturally gravitated to a high-level construct in the AWS CDK to help me build up a lot of infrastructure I really don’t want to worry about (VPCs, security groups, route tables, and all that other fun stuff us serverless guys hate to configure).

Luckily for me, the AWS CDK has an excellent high-level construct I can use called ApplicationLoadBalancedFargateService! For my needs, I need both an HTTP listener that forwards to the Fargate service, as well as an HTTPS one (and no, my HTTP listener doesn’t forward to HTTPS). Unfortunately, the documentation in the SDK doesn’t provide much assistance here on adding additional listeners to the infrastructure it creates, but luckily you found this blog!

In my implementation, I have an existing hosted zone in Route53 where I want to create my DNS record, so I am utilizing the HostedZone.fromLookup() helper method. You may want to create the zone yourself if you don’t already have one, or look up using a different method.

I’m also using DNS-validated certificates, feel free to use regular certificate validation if this method doesn’t work for you.

Finally, my container image exists in the public docker registry, you may want to replace the image property with some other method, potentially pointing to your own image in ECR.

The following snippet gets added to your lib/*-stack.ts file

const myZone = HostedZone.fromLookup(this, 'myZone', { domainName: 'example.tools' });

const certificate = new Certificate(this, 'cert', {
  domainName: 'httpbin.example.tools',
  certificateName: 'httpbin-certificate',
  validation: CertificateValidation.fromDns(myZone)
});

const fargateService = new ApplicationLoadBalancedFargateService(this, 'HttpBin', {
  taskImageOptions: {
    image: ContainerImage.fromRegistry('mccutchen/go-httpbin:latest'),
    environment: {
      PORT: '80'
    }
  },
  publicLoadBalancer: true,
  domainName: 'httpbin.example.tools',
  domainZone: myZone,
  listenerPort: 80,
  loadBalancerName: 'httpbin-go-alb',
  openListener: true,
  protocol: ApplicationProtocol.HTTP,
  recordType: ApplicationLoadBalancedServiceRecordType.ALIAS,
  targetProtocol: ApplicationProtocol.HTTP,
});

const fargateServiceTarget = ApplicationTargetGroup.fromTargetGroupAttributes(this, 'FargateTarget', {
  targetGroupArn: fargateService.targetGroup.targetGroupArn
});

fargateService.loadBalancer.addListener('https', {
  port: 443,
  protocol: ApplicationProtocol.HTTPS,
  certificates: [certificate],
  sslPolicy: SslPolicy.TLS12_EXT,
  defaultTargetGroups: [
    fargateServiceTarget
  ]
});

The secret sauce here for adding a listener to your ApplicationLoadBalancedFargateService is that the Application Load Balancer interface is available via the loadBalancer property of a ApplicationLoadBalancedFargateService which provides us access to the addListener() method. As well, the ApplicationTargetGroup.fromTargetGroupAttributes() helper function returns an IApplicationTargetGroup which can be passed into the defaultTargetGroups property of the addListener() props.

Of course, there’s nothing stopping you from doing this the other way around, adding the HTTPS listener to the ApplicationLoadBalancedFargateService and adding the HTTP listener, in my case I just happened to be tackling the HTTP use case first before I came back and added the HTTPS listener.

A few other things you may want to change after copying this verbatim:

  • The recordType property – an A ALIAS record fits my needs, it may not fit yours
  • The sslPolicy property of the listener, TLS12_EXT (aka ELBSecurityPolicy-TLS-1-2-Ext-2018-06) works for my use case, it may exclude legacy client support for you

Otherwise, happy CDK-ing!


Posted

in

, , ,

by

Tags: