question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

[Question]: Is it possible to add listeners (and target groups) to an existing ALB?

See original GitHub issue

Is it possible to add listeners (and target groups) to an existing ALB? I’m getting an error Error: Can only call addTargets() when using a constructed Load Balancer; construct a new TargetGroup and use addTargetGroup. What is the best way to share an existing ALB between (in my case Fargate) projects then?

From Fargate docs: https://awslabs.github.io/aws-cdk/refs/_aws-cdk_aws-ecs.html#include-a-load-balancer

I’m trying to do something similar as @nathanpeck did in https://github.com/nathanpeck/greeter-cdk

My target groups would be separate projects (i.e. /app1, /app2, etc) behind a shared ALB.

EDIT: This issue also relates to #1948. Our security team prevents us from creating roles, policies, SG, etc. So even if we could create an ALB (using elbv2.ApplicationLoadBalancer) we cannot perform security group changes that cdk is trying to introduce. Kinda catch 22. 😞

    //Import existing VPC
    const vpc = ec2.VpcNetwork.importFromContext(this, "VPC", {
      vpcId: props.vpcId    
    });

    //Import existing Security Group
    const sg = ec2.SecurityGroup.import(this, "SecurityGroup", {
      securityGroupId: props.securityGroupId
    });

    //Import ECS cluster
    const cluster = ecs.Cluster.import(this, "Cluster", {
      clusterName: props.clusterName,
      securityGroups: [ sg ],
      vpc: {
        vpcId: props.vpcId,
          availabilityZones: props.availabilityZones,
          privateSubnetIds: props.subnets        
      }
    });

    //Import existing taskRole
    const taskRole = iam.Role.import(this, "TaskRole", {
      roleArn: `arn:aws:iam::${props.accountId}:role/csr-Task-Role`
    });

    //Import existing executionRole
    const executionRole = iam.Role.import(this, "ExecutionRole", {
      roleArn: `arn:aws:iam::${props.accountId}:role/csr-Execution-Role`
    });

    //Import existing ELB
    const elb = elbv2.ApplicationLoadBalancer.import(this, "ALB", {
      loadBalancerArn: props.loadBalancerArn,
      securityGroupId: props.securityGroupId
    });

    //Create ECS Task definition
    const fargateTaskDefinition = new ecs.FargateTaskDefinition(this, 'TaskDefinition', {
      memoryMiB: "512",
      cpu: "256",
      taskRole: taskRole,
      executionRole: executionRole
    });

    //Create a container for task definition
    const container = fargateTaskDefinition.addContainer("Container", {
      // Use an image from DockerHub
      image: ecs.ContainerImage.fromDockerHub(props.dockerRepositoryUrl)
    });

    container.addPortMappings({
      containerPort: props.port
    })

    //Create fargate service with corresponding task definition
    const fargateService = new ecs.FargateService(this, "Service", {
      cluster: cluster,
      taskDefinition: fargateTaskDefinition,
      desiredCount: 1,
      securityGroup: sg
    });

    const lst = elb.addListener("Listener", {
      port: props.port
    });

    lst.addTargetGroups("default", {
        targetGroups: [ 
          new elbv2.ApplicationTargetGroup(this, "default", {
            vpc: vpc,
            protocol: elbv2.ApplicationProtocol.Http,
            port: props.port
          })
        ]
    });

    lst.addTargets("demo", {
      port: props.port,
      pathPattern: props.path,
      priority: props.priority,
      targets: [ fargateService ]
    })

Issue Analytics

  • State:closed
  • Created 5 years ago
  • Reactions:7
  • Comments:8 (8 by maintainers)

github_iconTop GitHub Comments

6reactions
robertdcommented, Mar 7, 2019

I somehow managed to make this work. Here is what I have ended up with…

    //Import existing VPC
    const vpc = ec2.VpcNetwork.importFromContext(this, "VPC", {
      vpcId: props.vpcId    
    });

    //Import existing Security Group
    const sg = ec2.SecurityGroup.import(this, "SecurityGroup", {
      securityGroupId: props.securityGroupId
    });

    //Import ECS cluster
    const cluster = ecs.Cluster.import(this, "Cluster", {
      clusterName: props.clusterName,
      securityGroups: [ sg ],
      vpc: {
        vpcId: props.vpcId,
          availabilityZones: props.availabilityZones,
          privateSubnetIds: props.subnets        
      }
    });

    //Import existing taskRole
    const taskRole = iam.Role.import(this, "TaskRole", {
      roleArn: `arn:aws:iam::${props.accountId}:role/csr-Task-Role`
    });

    //Import existing executionRole
    const executionRole = iam.Role.import(this, "ExecutionRole", {
      roleArn: `arn:aws:iam::${props.accountId}:role/csr-Execution-Role`
    });

    //Import existing applicationListener
    const applicationListener = elbv2.ApplicationListener.import(this, "ALBListener", {
      listenerArn: props.listenerArn,
      securityGroupId: sg.securityGroupId
    });

    const tg = new elbv2.CfnTargetGroup(this, "TargetGroup", {
      healthCheckPath: "/",
      healthCheckPort: "80",
      healthCheckProtocol: elbv2.Protocol.Http,
      healthCheckIntervalSeconds: 10,
      healthCheckTimeoutSeconds: 5,
      healthyThresholdCount: 2,
      unhealthyThresholdCount: 2, 
      port: props.port,
      protocol: elbv2.Protocol.Http,
      targetType: elbv2.TargetType.Ip,
      vpcId: vpc.vpcId      
    });

    new elbv2.CfnListenerRule(this, "ListenerRule", {
      listenerArn: applicationListener.listenerArn,
      priority: props.priority,
      conditions: [
        { field: "path-pattern", values: [ props.path ] }
      ],
      actions: [ { targetGroupArn: tg.targetGroupArn, type: "forward" } ]
    });

    //Create ECS Task definition
    const fargateTaskDefinition = new ecs.FargateTaskDefinition(this, 'TaskDefinition', {
      memoryMiB: "512",
      cpu: "256",
      taskRole: taskRole,
      executionRole: executionRole
    });

    //Create a container for task definition
    const container = fargateTaskDefinition.addContainer("Container", {
      // Use an image from DockerHub
      image: ecs.ContainerImage.fromDockerHub(props.dockerRepositoryUrl)
    });

    container.addPortMappings({
      containerPort: props.port
    });

    //Create fargate service with corresponding task definition
    const fargateService = new ecs.FargateService(this, "Service", {
      cluster: cluster,
      taskDefinition: fargateTaskDefinition,
      desiredCount: 1,
      securityGroup: sg
    });

    const resource = fargateService.node.findChild('Service') as ecs.CfnService;

    //Shove in loadBalancers override
    resource.propertyOverrides.loadBalancers = [{
      containerName: "Container",
      containerPort: props.port,
      targetGroupArn: tg.targetGroupArn
    }];

It feels sooooo hacky to me (mixing L1 and L2 constructs) and I hope that in the future cdk will be more friendlier towards limited AWS environments (reusable ALBs, SG, roles etc).

If there is more cleaner way to do this please let me know.

1reaction
robertdcommented, Mar 6, 2019

@rix0rrr I’ve implemented your suggestions (see code below).

    ...
    //Create fargate service with corresponding task definition
    const fargateService = new ecs.FargateService(this, `${props.appName}Service`, {
      cluster: cluster,
      taskDefinition: fargateTaskDefinition,
      desiredCount: props.desiredCount,
      securityGroup: sg
    });

    const tg = new elbv2.ApplicationTargetGroup(this, `${props.appName}TargetGroup`, {
      healthCheck: {
        path: "/",
        port: "80",
        protocol: elbv2.Protocol.Http
      },
      port: props.port,
      protocol: elbv2.ApplicationProtocol.Http,
      targetType: elbv2.TargetType.Ip,
      vpc: vpc
    });

    applicationListener.addTargetGroups(`${props.appName}TargetGroup`, {
       targetGroups: [ tg ],
       priority: props.priority,
       pathPattern: props.path
    });

    tg.addTarget(fargateService);

However, now during deployment I’m being asked to approve SG changes.

image

When I implement your ingress/egress no-op suggestions from #1948 I’m still getting SG change when I try to deploy. If you recall, I work in an environment where I’m unable to create SGs, roles, policies, etc.

    ...
    //Import existing Security Group
    const sg = ec2.SecurityGroup.import(this, "SecurityGroup", {
      securityGroupId: props.securityGroupId
    });

    sg.addIngressRule = () => {};
    sg.addEgressRule = () => {};

    ...

    //Import existing taskRole
    const taskRole = iam.Role.import(this, "TaskRole", {
      roleArn: `arn:aws:iam::${props.accountId}:role/csr-Task-Role`
    });

    taskRole.addToPolicy = () => {};

    //Import existing executionRole
    const executionRole = iam.Role.import(this, "ExecutionRole", {
      roleArn: `arn:aws:iam::${props.accountId}:role/csr-Execution-Role`
    });

    executionRole.addToPolicy = () => {};

And here is what cdk deploy is trying to perform, so no-op approach is definitely doing something but only for ingress.

image

Again, thank you so much for helping me figure this one out (and all other Github issues).

Read more comments on GitHub >

github_iconTop Results From Across the Web

Target groups for your Application Load Balancers
Each target group is used to route requests to one or more registered targets. When you create each listener rule, you specify a...
Read more >
AWS - Add existing load balancer to target group
Select a load balancer; Go to "Listeners" tab; Either edit an existing listener or add a new listener; Set rules and conditions, and...
Read more >
How the Application Load Balancer works - tecRacer
How are target groups populated? It depends. I know, it's frustrating. You can manually add endpoints to them or integrate AutoScaling, ECS ...
Read more >
Unable to delete Target Group. | AWS re:Post
Error: Target group 'arn:aws:elasticloadbalancing:xxxxxxx' is currently in use by a listener or a rule. But there is no LB associated with that target...
Read more >
How to use the AWS Load Balancer Controller to connect ...
Thus, the problem statement can be summarised as: how to manage AWS ... and ALBs with Terraform, and attach EKS nodes to ALB...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found