0
votes

Sorry for long post but hope that will provide good background. Do not know if that is a bug or my code is wrong. I want to create ECS cluster with EC2 spot instances with help of launch template and ASG. My code is as follows:

For ECS service, cluster, task definition:

resource "aws_ecs_cluster" "main" {
  name = "test-ecs-cluster"
}

resource "aws_ecs_service" "ec2_service" {
  for_each = data.aws_subnet_ids.all_subnets.ids
  name                              = "myservice_${replace(timestamp(), ":", "-")}"
  task_definition                   = aws_ecs_task_definition.task_definition.arn
  cluster                           = aws_ecs_cluster.main.id
  desired_count                     = 1
  launch_type                       = "EC2"
  health_check_grace_period_seconds = 10

  load_balancer {
    container_name   = "test-container"
    container_port   = 80
    target_group_arn = aws_lb_target_group.alb_ec2_ecs_tg.id
  }

  network_configuration {
    security_groups  = [aws_security_group.ecs_ec2.id]
    subnets          = [each.value]
    assign_public_ip = "false"
  }

  ordered_placement_strategy {
    type  = "binpack"
    field = "cpu"
  }
}

resource "aws_ecs_task_definition" "task_definition" {
  container_definitions    = data.template_file.task_definition_template.rendered
  family                   = "test-ec2-task-family"
  execution_role_arn       = aws_iam_role.ecs_task_exec_role_ec2_ecs.arn
  task_role_arn            = aws_iam_role.ecs_task_exec_role_ec2_ecs.arn
  network_mode             = "awsvpc"
  memory                   = 1024
  cpu                      = 1024
  requires_compatibilities = ["EC2"]

  lifecycle {
    create_before_destroy = true
  }
}

data "template_file" "task_definition_template" {
  template = file("${path.module}/templates/task_definition.json.tpl")
  vars = {
    container_port = var.container_port
    region         = var.region
    log_group      = var.cloudwatch_log_group
  }
}

Launch template:

resource "aws_launch_template" "template_for_spot" {
  name = "test-spor-ecs-launch-template"
  disable_api_termination = false
  instance_type = "t3.small"
  image_id = data.aws_ami.amazon_linux_2_ecs_optimized.id
  key_name = "FrankfurtRegion"
  user_data = data.template_file.user_data.rendered
  vpc_security_group_ids = [aws_security_group.ecs_ec2.id]
  monitoring {
    enabled = var.enable_spot == "true" ? false : true
  }
  block_device_mappings {
    device_name = "/dev/sda1"
    ebs {
      volume_size = 30
    }
  }
  iam_instance_profile {
    arn = aws_iam_instance_profile.ecs_instance_profile.arn
  }
  lifecycle {
    create_before_destroy = true
  }
}

data "template_file" "user_data" {
  template = file("${path.module}/user_data.tpl")
  vars = {
    cluster_name = aws_ecs_cluster.main.name
  }
}

ASG with scaling policy:

resource "aws_autoscaling_group" "ecs_spot_asg" {
  name = "test-asg-for-ecs"
  max_size = 4
  min_size = 2
  desired_capacity = 2
  termination_policies = [
    "OldestInstance"]
  vpc_zone_identifier = data.aws_subnet_ids.all_subnets.ids
  health_check_type = "ELB"
  health_check_grace_period = 300

  mixed_instances_policy {
    instances_distribution {
      on_demand_percentage_above_base_capacity = 0
      spot_instance_pools = 2
      spot_max_price = "0.03"
    }
    launch_template {
      launch_template_specification {
        launch_template_id = aws_launch_template.template_for_spot.id
        version = "$Latest"
      }
      override {
        instance_type = "t3.large"
      }
      override {
        instance_type = "t3.medium"
      }
      override {
        instance_type = "t3a.large"
      }
      override {
        instance_type = "t3a.medium"
      }
    }
  }
  lifecycle {
    create_before_destroy = true
  }
}

resource "aws_autoscaling_policy" "ecs_cluster_scale_policy" {
  autoscaling_group_name = aws_autoscaling_group.ecs_spot_asg.name
  name = "test-ecs-cluster-scaling-policy"
  policy_type = "TargetTrackingScaling"
  adjustment_type = "ChangeInCapacity"

  target_tracking_configuration {
    target_value = 70
    customized_metric_specification {
      metric_name = "ECS-cluster-metric"
      namespace = "AWS/ECS"
      statistic = "Average"
      metric_dimension {
        name = aws_ecs_cluster.main.name
        value = aws_ecs_cluster.main.name
      }
    }
  }
}

EDIT: I'm getting :

Error: InvalidParameterException: Creation of service was not idempotent. "test-ec2-service-qaz"

on ecs.tf line 5, in resource "aws_ecs_service" "ec2_service": 5: resource "aws_ecs_service" "ec2_service" {

EDIT2: Changed ecs_service name to name = "myservice_${replace(timestamp(), ":", "-")}", still getting same error.

Was reading from other issues that it is becouse of usage lifecycle with create_before_destroy statement in ecs_service, but it is not declared in my code. Maybe it is something related to something else, can't say what.

1
Can you share the full error with the surrounding context instead of truncating it like you have?ydaetskcoR
Edited question with errorMaciej
seems like you made some changes in service, stop the service or do redeploy will fix the issueAdiii
No didn't change anythinh, it is appearing during first deploy, no infrastructure on AWS accountMaciej
A wild guess: Terraform complains about code on line 5. That line contains the for_each statement. I would try with count instead of for_each because it seems that you want to create ECS service in each of the subnets available. Also, if that doesn't work, it might be that you need to change the way name is assigned to the service, appending a parameter that changes dynamically, like the subnet ID.Marko E

1 Answers

0
votes

Thanks @Marko E and @karnauskas on github with name = "myservice_${each.value}" was able to deploy three ECS services. With correction to sub-nets handling I was able to deploy all the "stuff" as required. Subnets:

data "aws_subnet_ids" "all_subnets" {
  vpc_id = data.aws_vpc.default.id
}

data "aws_subnet" "subnets" {
  for_each = data.aws_subnet_ids.all_subnets.ids
  id = each.value
}