The target group does not have an associated load balancer

Viewed 21128

I want to create ECS service from Cloud Formation Script. The service needs to be exposed to outside with Application Load Balancer

I have created Elastic Load Balancer, a Listener and ListnerRule

 Resources:
  Vpc:
    Type: 'AWS::EC2::VPC'
    Properties:
      CidrBlock: 10.0.0.0/16
      EnableDnsSupport: 'true'
      EnableDnsHostnames: 'true'
    Metadata:
      'AWS::CloudFormation::Designer':
        id: 0e3933ae-23c2-44e1-a0d9-82fcfba93511
  PubSubnetAz1:
    Type: 'AWS::EC2::Subnet'
    Properties:
      VpcId: !Ref Vpc
      CidrBlock: 10.0.1.0/24
      AvailabilityZone: 'ap-southeast-1a'
      MapPublicIpOnLaunch: true
    Metadata:
      'AWS::CloudFormation::Designer':
        id: 6c7ca021-4114-4ec8-acf8-4f103ff7011f
  PubSubnetAz2:
    Type: 'AWS::EC2::Subnet'
    Properties:
      VpcId: !Ref Vpc
      CidrBlock: 10.0.2.0/24
      AvailabilityZone: 'ap-southeast-1b'
      MapPublicIpOnLaunch: true
    Metadata:
      'AWS::CloudFormation::Designer':
        id: cfe07e5c-e00f-4918-b877-f567fa08c802
  InternetGateway:
    Type: 'AWS::EC2::InternetGateway'
    Metadata:
      'AWS::CloudFormation::Designer':
        id: 46bddd21-3027-4ccb-9e5d-ebf887429453
  AttachGateway:
    Type: 'AWS::EC2::VPCGatewayAttachment'
    Properties:
      VpcId: !Ref Vpc
      InternetGatewayId: !Ref InternetGateway
    Metadata:
      'AWS::CloudFormation::Designer':
        id: 11b7e802-d5ba-437a-8695-4bd5406d4db7
  RouteViaIgw:
    Type: 'AWS::EC2::RouteTable'
    Properties:
      VpcId: !Ref Vpc
    Metadata:
      'AWS::CloudFormation::Designer':
        id: 1a2f2b53-09d3-4c2c-8286-295870b8c602
  PublicRouteViaIgw:
    Type: 'AWS::EC2::Route'
    DependsOn:
      - AttachGateway
    Properties:
      RouteTableId: !Ref RouteViaIgw
      DestinationCidrBlock: 0.0.0.0/0
      GatewayId: !Ref InternetGateway
    Metadata:
      'AWS::CloudFormation::Designer':
        id: 4b7c941a-8498-4e70-886b-9339018cc18a
  PubSubnet1RouteTableAssociation:
    Type: 'AWS::EC2::SubnetRouteTableAssociation'
    Properties:
      SubnetId: !Ref PubSubnetAz1
      RouteTableId: !Ref RouteViaIgw
    Metadata:
      'AWS::CloudFormation::Designer':
        id: cea0d60a-6d91-4922-90ea-f6db9f4378a9


  PubSubnet2RouteTableAssociation:
    Type: 'AWS::EC2::SubnetRouteTableAssociation'
    Properties:
      SubnetId: !Ref PubSubnetAz2
      RouteTableId: !Ref RouteViaIgw
    Metadata:
      'AWS::CloudFormation::Designer':
        id: c3b3c8e1-a9c8-47c6-8d26-b6f272bcd9e1

  EcsSecurityGroup:
    Condition: CreateNewSecurityGroup
    Type: 'AWS::EC2::SecurityGroup'
    Properties:
      GroupDescription: ECS Allowed Ports
      SecurityGroupIngress:
        IpProtocol: tcp
        FromPort: 30
        ToPort: 150
        CidrIp: 0.0.0.0/0
    Metadata:
      'AWS::CloudFormation::Designer':
        id: 493fdb30-54ce-4e4e-9cd6-c9faa6e3f93b

  LoadBalancerSecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      VpcId: !Ref Vpc
      GroupDescription: Access to the load balancer that sits in front of ECS
      SecurityGroupIngress:
        # Allow access from anywhere to our ECS services
        - CidrIp: 0.0.0.0/0
          IpProtocol: -1


  ECSCluster:
    Type: AWS::ECS::Cluster
    Properties:
      ClusterName: 'my-cluster'

  EcsInstanceAsg:
    DependsOn: ECSCluster
    Type: 'AWS::AutoScaling::AutoScalingGroup'
    Properties:
      LaunchConfigurationName: !Ref EcsInstanceLc
      AvailabilityZones:
        - ap-southeast-1a
        - ap-southeast-1b
      MinSize: '2'
      MaxSize: '3'
      DesiredCapacity: '2'

      Tags:
        - Key: Name
          Value: !Sub 'ECS Instance stack'
          PropagateAtLaunch: 'true'
        - Key: Description
          Value: >-
            This instance is the part of the Auto Scaling group which was
            created through ECS Console
          PropagateAtLaunch: 'true'
    Metadata:
      'AWS::CloudFormation::Designer':
        id: 80731e0b-a9e5-461c-9049-e215aed2ad3d

  EcsInstanceLc:
      # DependsOn
      Type: 'AWS::AutoScaling::LaunchConfiguration'
      Properties:
        ImageId: 'ami-050865a806e0dae53'
        InstanceType: 't2.large'
        # AssociatePublicIpAddress: false
        SecurityGroups:
          - !Ref EcsSecurityGroup

      Metadata:
        'AWS::CloudFormation::Designer':
          id: 0e8e3b5a-7b14-4ffc-92af-ef9be7e51689

  LoadBalancer:
    Type: AWS::ElasticLoadBalancingV2::LoadBalancer
    Properties:
      Name: 'my-load-balancer'
      Subnets:
        - !Ref PubSubnetAz1
        - !Ref PubSubnetAz2
      Listeners:
        InstancePort: 80
        InstanceProtocol: HTTP
        LoadBalancerPort: 80
        Protocol: HTTP
      HealthCheck:
        HealthyThreshold: '10'
        Interval: '5'
        Target: '10'
        Timeout: '15'
        UnhealthyThreshold: '20'
      SecurityGroups: !Ref LoadBalancerSecurityGroup  



      Tags:
        - Key: Name
          Value: !Ref EcsClusterNam

  TargetGroup:
    Type: AWS::ElasticLoadBalancingV2::TargetGroup
    Properties:
      VpcId: !Ref Vpc
      Port: 80
      Protocol: HTTP
      HealthCheckIntervalSeconds: 10
      HealthCheckPort: 80
      HealthCheckPath: '/actuator/health'
      HealthCheckProtocol: HTTP
      HealthCheckTimeoutSeconds: 5
      HealthyThresholdCount: 3
      TargetType: instance
      Matcher:
        HttpCode: '200'

  Listener:
    Type: AWS::ElasticLoadBalancingV2::Listener
    Properties:
      DefaultActions:
      - Type: forward
        TargetGroupArn: !Ref TargetGroup
      LoadBalancerArn: !Ref LoadBalancer
      Port: '80'
      Protocol: HTTP


  ListenerRule:
    Type: AWS::ElasticLoadBalancingV2::ListenerRule
    Properties:
      Actions:
       - Type: forward
      TargetGroupArn: !Ref TargetGroup
      LoadBalancerArn: !Ref LoadBalancer
      Port: 80
      Protocol: tcp
      Priority: 1
      Conditions:
      - Field: path-pattern
        Values: "/*"
      ListenerArn: !Ref Listener

  TaskDefinition:
    Type: AWS::ECS::TaskDefinition
    Properties:
      Family: 'product-service'
      Cpu: '256'
      Memory: '512'
      NetworkMode: bridge
      RequiresCompatibilities:
        - EC2
      ExecutionRoleArn: 'ecserviceRole'
      ContainerDefinitions:
        - Name: 'product-service'
          Cpu: '128'
          Memory: '256'
          Image: 'ccmcwolf/microservices:awstest'
          PortMappings:
            - HostPort: '80'
              ContainerPort: '80'
              Protocol: 'tcp'

          # LogConfiguration:
          #   LogDriver: awslogs
          #   Options:
          #     awslogs-group: !Ref CloudWatchLogsGroup
          #     awslogs-region: !Ref AWS::Region


  Service:
    Type: AWS::ECS::Service
    Properties:
      Cluster: !Ref ECSCluster
      Role: 'ecserviceRole'
      DesiredCount: '2'
      TaskDefinition: !Ref TaskDefinition
      LoadBalancers:
        - TargetGroupArn: !Ref TargetGroup
          ContainerPort: 80
          ContainerName: "product-service"

but when I run the script in Cloudformation it throws an error

The target group with targetGroupArn arn:aws:elasticloadbalancing:ap-southeast-1:xxxxxxxxxxxx:targetgroup/iy6-TargetG-A6FWY20V6B6P/7e5d12f4cb4758a1 does not have an associated load balancer

. Entire AWS Cloud formation script available at https://notepad.pw/ecsaws

3 Answers

This case is now covered on the examples section of AWS::ECS::Service CloudFormation page, under "Associate an Application Load Balancer with a service".

The Amazon ECS service requires an explicit dependency on the Application Load Balancer listener rule and the Application Load Balancer listener. This prevents the service from starting before the listener is ready.

The ultimate issue that you have is due to AWS::ECS::Service trying to attach to the target group before the target group is added to the load balancer. The fix for that is very easy:

 Service:
    Type: AWS::ECS::Service
    DependsOn: Listener       # Line Added
    Properties:
      Cluster: !Ref ECSCluster
      Role: 'ecserviceRole'
      DesiredCount: '2'
      TaskDefinition: !Ref TaskDefinition
      LoadBalancers:
        - TargetGroupArn: !Ref TargetGroup
          ContainerPort: 80
          ContainerName: "product-service"

With that being said, you will also have to update your LoadBalancer definition because it has a lot of errors. It should be:

  LoadBalancer:
    Type: AWS::ElasticLoadBalancingV2::LoadBalancer
    Properties:
      Name: 'my-load-balancer'
      Subnets:
        - !Ref PubSubnetAz1
        - !Ref PubSubnetAz2
      SecurityGroups: 
        - !Ref LoadBalancerSecurityGroup 

Add DependsOn not just Listener but also the ListenerRule like so:

ECSService:
  Type: AWS::ECS::Service
  DependsOn:
    - ListenerHTTPS
    - ListenerRule

I experienced a similar issue when trying to create an ECS service that will be attached to a Load balancer using Terraform.

I created the Elastic Load balancer and the Target Groups in another module. When I try to create the ECS service that will be attached to a Load balancer I get the error:

Error: error updating ECS Service (arn:aws:ecs:eu-west-1:334691987455:service/notification/notification-private): InvalidParameterException: The target group with targetGroupArn arn:aws:elasticloadbalancing:eu-west-1:334691987455:targetgroup/notification/7d5c00c515fdc157 does not have an associated load balancer.

Here's how I fixed it:

In creating an ECS service that will be attached to a Load balancer, you will need to create the Load Balancer Listener, Target Group for the service as well as the Listener Rule for the ECS service.

It's the Listener Rule for the ECS service that attaches the Target Group to the Load Balancer.

In my case, I had created the Load Balancer Listener, Target Group, but had not created the Listener Rule for the ECS service. All I had to do was to create the Listener Rule for the ECS service, after which I tried to create the Listener Rule for the ECS service, and this time it worked fine.

Note: If you creating the Load Balancer Listener, Target Group and Listener Rule in the same module as the ECS service then you will need to add a dependency in the ECS service so that the ECS service can wait until the Listener, Target Group and Listener Rule are created successfully.

Listener rule

module "lb_listener_rule_my_app_ssl" {
  source = "../../../../modules/aws/lb-listener-rule"

  listener_arn         = module.lb_listener_alb_public_443.lb_listener_arn
  listener_priority    = var.listener_priority.84
  listener_action_type = var.listener_action_type.forward
  lb_target_group_arn  = module.lb_target_group_my_app.lb_target_group_arn
  condition_host_headers = [
    {
      condition_host_header_values = var.condition_host_headers[6].condition_host_header_values
    }
  ]
  tag_environment = local.tag_environment
  tag_terraform   = local.tag_terraform.true

  depends_on = [module.lb_listener_alb_public_443]
}

ECS Service

module "ecs_service_my_app" {
  source = "../../../../modules/aws/ecs-service"

  ecs_service_name                   = var.ecs_service_name.my_app
  ecs_service_cluster_id             = module.ecs_cluster_my_app.ecs_cluster_id
  ecs_service_task_definition_arn    = module.ecs_task_definition_my_app.ecs_task_definition_arn
  desired_count                      = var.desired_count
  deployment_minimum_healthy_percent = var.deployment_minimum_healthy_percent
  deployment_maximum_percent         = var.deployment_maximum_percent
  platform_version                   = var.platform_version
  launch_type                        = var.launch_type
  scheduling_strategy                = var.scheduling_strategy
  force_new_deployment               = var.force_new_deployment
  ecs_service_security_groups        = [data.aws_security_groups.my_app.ids[0]]
  ecs_service_subnets                = [data.aws_subnets.private.ids[0], data.aws_subnets.private.ids[1], data.aws_subnets.private.ids[2]]
  assign_public_ip                   = var.assign_public_ip
  load_balancer_rules = [
    {
      target_group_arn = data.aws_lb_target_group.my_app.arn
      container_name   = var.container_name.my_app
      container_port   = var.container_port[0]
    }
  ]
  tag_environment = local.tag_environment
  tag_terraform   = local.tag_terraform.true

  depends_on = [module.aws_lb_listener.alb_public_80]
}
Related