<p>The underlaying issue was an IAM permission error. After clicking through the stack, the &quot;tasks&quot; in the cluster didn't start because of missing permissions/roles.</p>

CC BY-SA 4.0
2021-12-06T19:58:09.250Z
2021-12-06T19:58:09.250Z
Patrick
<p>I've had the same case. After hours of digging around Load Balancer, Target Group, Listener and Cluster/Service configurations, discovered the reason. It was about the <strong>production port configured in the Deployment Group</strong>.</p>
<p>A Blue/green deployment expects the Load Balancer's listener for the app's &quot;production port&quot; to be directed to a target group so it can change that group with the other one.</p>
<p>In my case, I had listeners for port 80, 90 (for test) and 443.</p>
<p>The Deployment Group was configured for port 80 as the production port but the load balancer's listener for port 80 did not have a target group behind it. All it had was a re-route to 443.</p>
<p>So the problem was about the Deployment Group configuration. I changed the production port in the Deployment Configuration to port 443 and everything started working.</p>

CC BY-SA 4.0
2024-09-09T14:18:39.303Z
2024-09-09T14:18:39.303Z
e-mre
<p>I've been trying for hours to get a basic CodePipeline up and running with ECS Green/Blue. We're creating a prototype, so everything is in a single repo for now, and the <code>Dockerfile</code>, <code>taskdef.json</code>, <code>appspec.yaml</code> are all in a subfolder. I'm getting the following error:</p>
<p><code>The ELB could not be updated due to the following error: Primary taskset target group must be behind listener arn:aws:elasticloadbalancing:eu-west-1:123:listener/app/api-lb/123/123</code></p>
<p>It's a standard pipeline:</p>
<ol>
<li>CodeCommit</li>
<li>CodeBuild</li>
<li>CodeDeploy to ECS</li>
</ol>
<p>Commit and Build work and I'm passing the artifacts to the last step. Deployment fails though, with the error mentioned above.</p>
<p>I have no idea what exactly the &quot;primary taskset target group&quot; is. Both target groups <em>are</em> behind a listener.</p>
<p>I know there are Terraform modules for this, but I wanted to modify/extend this after it works, so I decided to start from scratch. Went through a dozen different articles, but I can't figure out what's wrong with my ECS config.</p>
<p>I created a repo with all the files <a href="https://github.com/zentered/terraform-ecs-green-blue-pipeline" rel="nofollow noreferrer">here</a>, here are the relevant the lb, ecs and CodeDeploy configs:</p>
<p><em>lb</em>:</p>
<pre><code>resource &quot;aws_lb&quot; &quot;lb&quot; {
  name               = &quot;${var.name}-lb&quot;
  internal           = false
  load_balancer_type = &quot;application&quot;
  subnets            = [aws_default_subnet.default_az1.id, aws_default_subnet.default_az2.id]
  security_groups    = [aws_security_group.blue_green.id]
}

resource &quot;aws_lb_target_group&quot; &quot;blue&quot; {
  name        = &quot;${var.name}-blue&quot;
  port        = 80
  target_type = &quot;ip&quot;
  protocol    = &quot;HTTP&quot;
  vpc_id      = aws_default_vpc.default.id
  depends_on = [
    aws_lb.lb
  ]
  health_check {
    path                = &quot;/&quot;
    protocol            = &quot;HTTP&quot;
    matcher             = &quot;200&quot;
    interval            = &quot;10&quot;
    timeout             = &quot;5&quot;
    unhealthy_threshold = &quot;3&quot;
    healthy_threshold   = &quot;3&quot;
  }
  tags = {
    Name = var.name
  }
}

resource &quot;aws_lb_target_group&quot; &quot;green&quot; {
  name        = &quot;${var.name}-green&quot;
  port        = 80
  target_type = &quot;ip&quot;
  protocol    = &quot;HTTP&quot;
  vpc_id      = aws_default_vpc.default.id
  depends_on = [
    aws_lb.lb
  ]
  health_check {
    path                = &quot;/&quot;
    protocol            = &quot;HTTP&quot;
    matcher             = &quot;200&quot;
    interval            = &quot;10&quot;
    timeout             = &quot;5&quot;
    unhealthy_threshold = &quot;3&quot;
    healthy_threshold   = &quot;3&quot;
  }
  tags = {
    Name = var.name
  }
}

resource &quot;aws_lb_listener&quot; &quot;green&quot; {
  load_balancer_arn = aws_lb.lb.arn
  port              = &quot;80&quot;
  protocol          = &quot;HTTP&quot;
  default_action {
    type             = &quot;forward&quot;
    target_group_arn = aws_lb_target_group.green.arn
  }
  depends_on = [aws_lb_target_group.green]
}

resource &quot;aws_lb_listener&quot; &quot;blue&quot; {
  load_balancer_arn = aws_lb.lb.arn
  port              = &quot;8080&quot;
  protocol          = &quot;HTTP&quot;
  default_action {
    type             = &quot;forward&quot;
    target_group_arn = aws_lb_target_group.blue.arn
  }
  depends_on = [aws_lb_target_group.blue]
}
</code></pre>
<p><em>codedeploy</em></p>
<pre><code>resource &quot;aws_codedeploy_app&quot; &quot;app&quot; {
  compute_platform = &quot;ECS&quot;
  name             = var.name
}

resource &quot;aws_codedeploy_deployment_group&quot; &quot;group&quot; {
  app_name               = var.name
  deployment_config_name = &quot;CodeDeployDefault.ECSAllAtOnce&quot;
  deployment_group_name  = &quot;${var.name}-group-1&quot;
  service_role_arn       = aws_iam_role.codedeploy-service.arn

  auto_rollback_configuration {
    enabled = true
    events  = [&quot;DEPLOYMENT_FAILURE&quot;]
  }

  blue_green_deployment_config {
    deployment_ready_option {
      action_on_timeout = &quot;CONTINUE_DEPLOYMENT&quot;
    }

    terminate_blue_instances_on_deployment_success {
      action = &quot;TERMINATE&quot;
    }
  }

  deployment_style {
    deployment_option = &quot;WITH_TRAFFIC_CONTROL&quot;
    deployment_type   = &quot;BLUE_GREEN&quot;
  }

  ecs_service {
    cluster_name = aws_ecs_cluster.cluster.name
    service_name = aws_ecs_service.api.name
  }

  load_balancer_info {
    target_group_pair_info {
      prod_traffic_route {
        listener_arns = [aws_lb_listener.green.arn]
      }

      test_traffic_route {
        listener_arns = [aws_lb_listener.blue.arn]
      }

      target_group {
        name = aws_lb_target_group.blue.name
      }

      target_group {
        name = aws_lb_target_group.green.name
      }
    }
  }
}
</code></pre>
<p><em>ecs</em>:</p>
<pre><code>resource &quot;aws_ecs_cluster&quot; &quot;cluster&quot; {
  name = var.name

  configuration {
    execute_command_configuration {
      kms_key_id = aws_kms_key.keys.arn
      logging    = &quot;OVERRIDE&quot;

      log_configuration {
        cloud_watch_encryption_enabled = true
        cloud_watch_log_group_name     = aws_cloudwatch_log_group.logs.name
      }
    }
  }
}

resource &quot;aws_ecs_task_definition&quot; &quot;api&quot; {
  family                   = &quot;api&quot;
  memory                   = 512
  cpu                      = 256
  requires_compatibilities = [&quot;FARGATE&quot;]
  network_mode             = &quot;awsvpc&quot;
  execution_role_arn       = aws_iam_role.ecs-task-service.arn

  container_definitions = jsonencode([
    {
      name      = var.name,
      image     = &quot;${aws_ecr_repository.repo.repository_url}:latest&quot;
      essential = true
      portMappings = [
        {
          &quot;containerPort&quot; = 8080,
          &quot;hostPort&quot;      = 8080
        }
      ],
      environment = [],
      log_options = {
        awslogs-region        = var.region
        awslogs-group         = var.name
        awslogs-stream-prefix = &quot;ecs-service&quot;
      }
    }
  ])
}

resource &quot;aws_ecs_service&quot; &quot;api&quot; {
  name            = var.name
  cluster         = aws_ecs_cluster.cluster.id
  task_definition = aws_ecs_task_definition.api.arn
  launch_type     = &quot;FARGATE&quot;
  desired_count   = 1
  depends_on      = [aws_lb_listener.green, aws_lb_listener.blue, aws_iam_role_policy.ecs-service-base-policy]

  deployment_controller {
    type = &quot;CODE_DEPLOY&quot;
  }

  load_balancer {
    target_group_arn = aws_lb_target_group.blue.arn
    container_name   = var.name
    container_port   = 8080
  }

  network_configuration {
    subnets = [aws_default_subnet.default_az1.id, aws_default_subnet.default_az2.id, aws_default_subnet.default_az3.id]
    security_groups = [
      aws_security_group.ecs.id
    ]
    assign_public_ip = false
  }

  lifecycle {
    ignore_changes = [
      load_balancer,
      desired_count,
      task_definition
    ]
  }
}
</code></pre>
<p>Any hints on what I'm doing wrong? Thanks!</p>

CC BY-SA 4.0
2021-12-03T19:55:51.697Z
Please clarify your specific problem or provide additional details to highlight exactly what you need. As it's currently written, it's hard to tell exactly what you're asking.
Community
<amazon-web-services><terraform><amazon-ecs><aws-codepipeline>
AWS ECS Green/Blue with CodePipeline: The ELB could not be updated due to the following error: Primary taskset target group must be behind listener
Patrick
# AWS ECS Green/Blue with CodePipeline: The ELB could not be updated due to the following error: Primary taskset target group must be behind listener

(Asked by: username_0 on 2021-12-03)

I've been trying for hours to get a basic CodePipeline up and running with ECS Green/Blue. We're creating a prototype, so everything is in a single repo for now, and the Dockerfile, taskdef.json, appspec.yaml are all in a subfolder. I'm getting the following error:
The ELB could not be updated due to the following error: Primary taskset target group must be behind listener arn:aws:elasticloadbalancing:eu-west-1:123:listener/app/api-lb/123/123
It's a standard pipeline:

CodeCommit
CodeBuild
CodeDeploy to ECS

Commit and Build work and I'm passing the artifacts to the last step. Deployment fails though, with the error mentioned above.
I have no idea what exactly the "primary taskset target group" is. Both target groups are behind a listener.
I know there are Terraform modules for this, but I wanted to modify/extend this after it works, so I decided to start from scratch. Went through a dozen different articles, but I can't figure out what's wrong with my ECS config.
I created a repo with all the files here, here are the relevant the lb, ecs and CodeDeploy configs:
lb:
resource "aws_lb" "lb" {
  name               = "${var.name}-lb"
  internal           = false
  load_balancer_type = "application"
  subnets            = [aws_default_subnet.default_az1.id, aws_default_subnet.default_az2.id]
  security_groups    = [aws_security_group.blue_green.id]
}

resource "aws_lb_target_group" "blue" {
  name        = "${var.name}-blue"
  port        = 80
  target_type = "ip"
  protocol    = "HTTP"
  vpc_id      = aws_default_vpc.default.id
  depends_on = [
    aws_lb.lb
  ]
  health_check {
    path                = "/"
    protocol            = "HTTP"
    matcher             = "200"
    interval            = "10"
    timeout             = "5"
    unhealthy_threshold = "3"
    healthy_threshold   = "3"
  }
  tags = {
    Name = var.name
  }
}

resource "aws_lb_target_group" "green" {
  name        = "${var.name}-green"
  port        = 80
  target_type = "ip"
  protocol    = "HTTP"
  vpc_id      = aws_default_vpc.default.id
  depends_on = [
    aws_lb.lb
  ]
  health_check {
    path                = "/"
    protocol            = "HTTP"
    matcher             = "200"
    interval            = "10"
    timeout             = "5"
    unhealthy_threshold = "3"
    healthy_threshold   = "3"
  }
  tags = {
    Name = var.name
  }
}

resource "aws_lb_listener" "green" {
  load_balancer_arn = aws_lb.lb.arn
  port              = "80"
  protocol          = "HTTP"
  default_action {
    type             = "forward"
    target_group_arn = aws_lb_target_group.green.arn
  }
  depends_on = [aws_lb_target_group.green]
}

resource "aws_lb_listener" "blue" {
  load_balancer_arn = aws_lb.lb.arn
  port              = "8080"
  protocol          = "HTTP"
  default_action {
    type             = "forward"
    target_group_arn = aws_lb_target_group.blue.arn
  }
  depends_on = [aws_lb_target_group.blue]
}

codedeploy
resource "aws_codedeploy_app" "app" {
  compute_platform = "ECS"
  name             = var.name
}

resource "aws_codedeploy_deployment_group" "group" {
  app_name               = var.name
  deployment_config_name = "CodeDeployDefault.ECSAllAtOnce"
  deployment_group_name  = "${var.name}-group-1"
  service_role_arn       = aws_iam_role.codedeploy-service.arn

  auto_rollback_configuration {
    enabled = true
    events  = ["DEPLOYMENT_FAILURE"]
  }

  blue_green_deployment_config {
    deployment_ready_option {
      action_on_timeout = "CONTINUE_DEPLOYMENT"
    }

    terminate_blue_instances_on_deployment_success {
      action = "TERMINATE"
    }
  }

  deployment_style {
    deployment_option = "WITH_TRAFFIC_CONTROL"
    deployment_type   = "BLUE_GREEN"
  }

  ecs_service {
    cluster_name = aws_ecs_cluster.cluster.name
    service_name = aws_ecs_service.api.name
  }

  load_balancer_info {
    target_group_pair_info {
      prod_traffic_route {
        listener_arns = [aws_lb_listener.green.arn]
      }

      test_traffic_route {
        listener_arns = [aws_lb_listener.blue.arn]
      }

      target_group {
        name = aws_lb_target_group.blue.name
      }

      target_group {
        name = aws_lb_target_group.green.name
      }
    }
  }
}

ecs:
resource "aws_ecs_cluster" "cluster" {
  name = var.name

  configuration {
    execute_command_configuration {
      kms_key_id = aws_kms_key.keys.arn
      logging    = "OVERRIDE"

      log_configuration {
        cloud_watch_encryption_enabled = true
        cloud_watch_log_group_name     = aws_cloudwatch_log_group.logs.name
      }
    }
  }
}

resource "aws_ecs_task_definition" "api" {
  family                   = "api"
  memory                   = 512
  cpu                      = 256
  requires_compatibilities = ["FARGATE"]
  network_mode             = "awsvpc"
  execution_role_arn       = aws_iam_role.ecs-task-service.arn

  container_definitions = jsonencode([
    {
      name      = var.name,
      image     = "${aws_ecr_repository.repo.repository_url}:latest"
      essential = true
      portMappings = [
        {
          "containerPort" = 8080,
          "hostPort"      = 8080
        }
      ],
      environment = [],
      log_options = {
        awslogs-region        = var.region
        awslogs-group         = var.name
        awslogs-stream-prefix = "ecs-service"
      }
    }
  ])
}

resource "aws_ecs_service" "api" {
  name            = var.name
  cluster         = aws_ecs_cluster.cluster.id
  task_definition = aws_ecs_task_definition.api.arn
  launch_type     = "FARGATE"
  desired_count   = 1
  depends_on      = [aws_lb_listener.green, aws_lb_listener.blue, aws_iam_role_policy.ecs-service-base-policy]

  deployment_controller {
    type = "CODE_DEPLOY"
  }

  load_balancer {
    target_group_arn = aws_lb_target_group.blue.arn
    container_name   = var.name
    container_port   = 8080
  }

  network_configuration {
    subnets = [aws_default_subnet.default_az1.id, aws_default_subnet.default_az2.id, aws_default_subnet.default_az3.id]
    security_groups = [
      aws_security_group.ecs.id
    ]
    assign_public_ip = false
  }

  lifecycle {
    ignore_changes = [
      load_balancer,
      desired_count,
      task_definition
    ]
  }
}

Any hints on what I'm doing wrong? Thanks!

**Tags:** amazon-web-services, terraform, amazon-ecs, aws-codepipeline

> **Comments:**
> **username_1**: Please clarify your specific problem or provide additional details to highlight exactly what you need. As it's currently written, it's hard to tell exactly what you're asking.  

---

## 2 answers

### Answer by username_0 on 2021-12-06. Score: 2 | Accepted answer

The underlaying issue was an IAM permission error. After clicking through the stack, the "tasks" in the cluster didn't start because of missing permissions/roles.


---

### Answer by username_2 on 2024-09-09. Score: 0

I've had the same case. After hours of digging around Load Balancer, Target Group, Listener and Cluster/Service configurations, discovered the reason. It was about the **production port configured in the Deployment Group**.

A Blue/green deployment expects the Load Balancer's listener for the app's "production port" to be directed to a target group so it can change that group with the other one.

In my case, I had listeners for port 80, 90 (for test) and 443.

The Deployment Group was configured for port 80 as the production port but the load balancer's listener for port 80 did not have a target group behind it. All it had was a re-route to 443.

So the problem was about the Deployment Group configuration. I changed the production port in the Deployment Configuration to port 443 and everything started working.


---

