Skip to content

Commit

Permalink
fix(elbv2): allow multiple certificates on ALB listener (#4116)
Browse files Browse the repository at this point in the history
* fix(elasticloadbalancingv2): Allow Multiple Certificates on ALB Listener

Fixes a cloudformation error when attaching multiple certificates to an alb listener. Cloudformation allows only a single certificate attached to the `LoadBalancer` resource. If multiple certificates are passed to the construct on initialization or through the `.addCertificateArns` method, separate `ListenerCertificate` resources are created for all after the first.

Closes [issue #3757](#3757)

* PR Revisions
  • Loading branch information
MrArnoldPalmer authored and mergify[bot] committed Sep 19, 2019
1 parent 899656c commit d1f8e5c
Show file tree
Hide file tree
Showing 2 changed files with 103 additions and 3 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,11 @@ export class ApplicationListener extends BaseListener implements IApplicationLis
this.loadBalancer = props.loadBalancer;
this.protocol = protocol;
this.certificateArns = [];
this.certificateArns.push(...(props.certificateArns || []));

// Attach certificates
if (props.certificateArns && props.certificateArns.length > 0) {
this.addCertificateArns("ListenerCertificate", props.certificateArns);
}

// This listener edits the securitygroup of the load balancer,
// but adds its own default port.
Expand All @@ -142,9 +146,25 @@ export class ApplicationListener extends BaseListener implements IApplicationLis

/**
* Add one or more certificates to this listener.
*
* After the first certificate, this creates ApplicationListenerCertificates
* resources since cloudformation requires the certificates array on the
* listener resource to have a length of 1.
*/
public addCertificateArns(_id: string, arns: string[]): void {
this.certificateArns.push(...arns);
public addCertificateArns(id: string, arns: string[]): void {
const additionalCertArns = [...arns];

if (this.certificateArns.length === 0 && additionalCertArns.length > 0) {
const first = additionalCertArns.splice(0, 1)[0];
this.certificateArns.push(first);
}

if (additionalCertArns.length > 0) {
new ApplicationListenerCertificate(this, id, {
listener: this,
certificateArns: additionalCertArns
});
}
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,30 @@ export = {
test.done();
},

'HTTPS listener can add certificate after construction'(test: Test) {
// GIVEN
const stack = new cdk.Stack();
const vpc = new ec2.Vpc(stack, 'Stack');
const lb = new elbv2.ApplicationLoadBalancer(stack, 'LB', { vpc });

// WHEN
const listener = lb.addListener('Listener', {
port: 443,
defaultTargetGroups: [new elbv2.ApplicationTargetGroup(stack, 'Group', { vpc, port: 80 })]
});

listener.addCertificateArns("Arns", ['cert']);

// THEN
expect(stack).to(haveResource('AWS::ElasticLoadBalancingV2::Listener', {
Certificates: [
{ CertificateArn: "cert" }
],
}));

test.done();
},

'Can configure targetType on TargetGroups'(test: Test) {
// GIVEN
const stack = new cdk.Stack();
Expand Down Expand Up @@ -647,6 +671,62 @@ export = {

test.done();
},

'Can pass multiple certificate arns to application listener constructor'(test: Test) {
// GIVEN
const stack = new cdk.Stack();
const vpc = new ec2.Vpc(stack, 'Stack');
const lb = new elbv2.ApplicationLoadBalancer(stack, 'LB', { vpc });

// WHEN
lb.addListener('Listener', {
port: 443,
certificateArns: ['cert1', 'cert2'],
defaultTargetGroups: [new elbv2.ApplicationTargetGroup(stack, 'Group', { vpc, port: 80 })]
});

// THEN
expect(stack).to(haveResource('AWS::ElasticLoadBalancingV2::Listener', {
Protocol: 'HTTPS'
}));

expect(stack).to(haveResource('AWS::ElasticLoadBalancingV2::ListenerCertificate', {
Certificates: [{ CertificateArn: 'cert2' }],
}));

test.done();
},

'Can add additional certificates via addCertficateArns to application listener'(test: Test) {
// GIVEN
const stack = new cdk.Stack();
const vpc = new ec2.Vpc(stack, 'Stack');
const lb = new elbv2.ApplicationLoadBalancer(stack, 'LB', { vpc });

// WHEN
const listener = lb.addListener('Listener', {
port: 443,
certificateArns: ['cert1', 'cert2'],
defaultTargetGroups: [new elbv2.ApplicationTargetGroup(stack, 'Group', { vpc, port: 80 })]
});

listener.addCertificateArns("ListenerCertificateX", ['cert3']);

// THEN
expect(stack).to(haveResource('AWS::ElasticLoadBalancingV2::Listener', {
Protocol: 'HTTPS'
}));

expect(stack).to(haveResource('AWS::ElasticLoadBalancingV2::ListenerCertificate', {
Certificates: [{ CertificateArn: 'cert2' }],
}));

expect(stack).to(haveResource('AWS::ElasticLoadBalancingV2::ListenerCertificate', {
Certificates: [{ CertificateArn: 'cert3' }],
}));

test.done();
},
};

class ResourceWithLBDependency extends cdk.CfnResource {
Expand Down

0 comments on commit d1f8e5c

Please sign in to comment.