-1

I tried to use terraform to setup aws codeploy ecs infrastructure, following aws documentation to understand aws deploy : https://docs.aws.amazon.com/AmazonECS/latest/developerguide/create-blue-green.html , reading this post to have an example (it uses EC2 instance) : https://hiveit.co.uk/techshop/terraform-aws-vpc-example/02-create-the-vpc/ and finally use reference into terraform documentation : https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/codedeploy_deployment_group

The probleme is when I tried to make a deploy from aws codedeploy, the deployment is stuck in the install phase

enter image description here

Here is the terraform configuration I have done

# main.tf
terraform {
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 4.0"
    }
  }
}

provider "aws" {
  # defined in AWS_REGION env
  # defined in AWS_ACCESS_KEY_ID env
  # defined in AWS_SECRET_ACCESS_KEY env
}

# create repository to store docker image
resource "aws_ecr_repository" "repository" {
  name = "test-repository"
}
# network.tf
resource "aws_vpc" "vpc" {
  cidr_block = "10.0.0.0/16"

  tags = {
    Name = "terraform-example-vpc"
  }
}

resource "aws_internet_gateway" "gateway" {
  vpc_id = aws_vpc.vpc.id

  tags = {
    Name = "terraform-example-internet-gateway"
  }
}

resource "aws_route" "route" {
  route_table_id         = aws_vpc.vpc.main_route_table_id
  destination_cidr_block = "0.0.0.0/0"
  gateway_id             = aws_internet_gateway.gateway.id
}

resource "aws_subnet" "main" {
  count                   = length(data.aws_availability_zones.available.names)
  vpc_id                  = aws_vpc.vpc.id
  cidr_block              = "10.0.${count.index}.0/24"
  map_public_ip_on_launch = true
  availability_zone       = element(data.aws_availability_zones.available.names, count.index)

  tags = {
    Name = "public-subnet-${element(data.aws_availability_zones.available.names, count.index)}"
  }
}
# loadbalancer.tf
resource "aws_security_group" "lb_security_group" {
  name        = "terraform_lb_security_group"
  vpc_id      = aws_vpc.vpc.id

  ingress {
    from_port   = 80
    to_port     = 80
    protocol    = "tcp"
    cidr_blocks = ["0.0.0.0/0"]
  }

  egress {
    from_port   = 0
    to_port     = 0
    protocol    = "-1"
    cidr_blocks = ["0.0.0.0/0"]
  }

  tags = {
    Name = "terraform-example-lb-security-group"
  }
}

resource "aws_lb" "lb" {
  name            = "terraform-example-lb"
  security_groups = [aws_security_group.lb_security_group.id]
  subnets         = aws_subnet.main.*.id

  tags = {
    Name = "terraform-example-lb"
  }
}

resource "aws_lb_target_group" "group1" {
  name        = "terraform-example-lb-target1"
  port        = 80
  protocol    = "HTTP"
  vpc_id      = aws_vpc.vpc.id
  target_type = "ip"
}

resource "aws_lb_listener" "listener_http" {
  load_balancer_arn = aws_lb.lb.arn
  port              = "80"
  protocol          = "HTTP"

  default_action {
    target_group_arn = aws_lb_target_group.group1.arn
    type             = "forward"
  }
}
# cluster.tf
resource "aws_ecs_cluster" "cluster" {
  name = "terraform-example-cluster"

  tags = {
    Name = "terraform-example-cluster"
  }
}

resource "aws_iam_role" "ecsTaskExecutionRole" {
  name = "ecsTaskExecutionRole"

  assume_role_policy = jsonencode({
    Version = "2012-10-17"
    Statement = [
      {
        "Sid" : "",
        "Effect" : "Allow",
        "Principal" : {
          "Service" : "ecs-tasks.amazonaws.com"
        },
        "Action" : "sts:AssumeRole"
      }
    ]
  })
}

resource "aws_ecs_task_definition" "task_definition" {
  family                   = "deployment-app"
  network_mode             = "awsvpc"
  requires_compatibilities = ["FARGATE"]
  cpu                      = 256
  memory                   = 512
  execution_role_arn       = aws_iam_role.ecsTaskExecutionRole.arn
  container_definitions = jsonencode([
    {
      "name" : "app",
      "image" : "httpd:2.4",
      "portMappings" : [
        {
          "containerPort" : 80,
          "hostPort" : 80,
          "protocol" : "tcp"
        }
      ],
      "essential" : true
    }
  ])
}

resource "aws_ecs_service" "service" {
  cluster             = aws_ecs_cluster.cluster.id
  name                = "terraform-example-service"
  task_definition     = "deployment-app"
  launch_type         = "FARGATE"
  scheduling_strategy = "REPLICA"
  platform_version    = "LATEST"
  desired_count       = 1

  load_balancer {
    target_group_arn = aws_lb_target_group.group1.arn
    container_name   = "app"
    container_port   = 80
  }

  deployment_controller {
    type = "CODE_DEPLOY"
  }

  network_configuration {
    assign_public_ip = true
    security_groups  = [aws_security_group.lb_security_group.id]
    subnets          = aws_subnet.main.*.id
  }

  lifecycle {
    ignore_changes = [desired_count, task_definition, platform_version]
  }
}
# codedeploy.tf
resource "aws_codedeploy_app" "codedeploy_app" {
  name             = "example-codedeploy-app"
  compute_platform = "ECS"
}

resource "aws_lb_target_group" "group2" {
  name        = "terraform-example-lb-target2"
  port        = 80
  protocol    = "HTTP"
  vpc_id      = aws_vpc.vpc.id
  target_type = "ip"
}

resource "aws_codedeploy_deployment_group" "codedeploy_group" {
  app_name               = aws_codedeploy_app.codedeploy_app.name
  deployment_group_name  = "deployment_group_name"
  service_role_arn       = "###"
  deployment_config_name = "CodeDeployDefault.ECSAllAtOnce"

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

  blue_green_deployment_config {
    deployment_ready_option {
      action_on_timeout    = "CONTINUE_DEPLOYMENT"
      wait_time_in_minutes = 0
    }

    terminate_blue_instances_on_deployment_success {
      action                           = "TERMINATE"
      termination_wait_time_in_minutes = 1
    }
  }

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

  load_balancer_info {
    target_group_pair_info {
      target_group {
        name = aws_lb_target_group.group1.name
      }

      target_group {
        name = aws_lb_target_group.group2.name
      }

      prod_traffic_route {
        listener_arns = [aws_lb_listener.listener_http.arn]
      }
    }
  }

  ecs_service {
    cluster_name = aws_ecs_cluster.cluster.name
    service_name = aws_ecs_service.service.name
  }
}
# datasource.tf
data "aws_availability_zones" "available" {}

note: replace ### with the arn of the role AWSCodeDeployRoleForECS : https://docs.aws.amazon.com/AmazonECS/latest/developerguide/codedeploy_IAM_role.html I don't add it into terraform yet

after using

terraform plan
terraform apply

all the stack is set and i have access to the it works of httpd through the load balancer dns name

enter image description here

My probleme is when I push a new image to the repository, update the task definition and create a new deployment, this last one is stuck in the Step 1 without any error or whatever

For the example, I tried to push an nginx image instead of httpd

aws ecs register-task-definition \
        --family=deployment-app \
        --network-mode=awsvpc \
        --cpu=256 \
        --memory=512 \
        --execution-role-arn=arn:aws:iam::__AWS_ACCOUNT__:role/ecsTaskExecutionRole \
        --requires-compatibilities='["FARGATE"]' \
        --container-definitions='[{"name": "app","image": "nginx:latest","portMappings": [{"containerPort": 80,"hostPort": 80,"protocol": "tcp"}],"essential": true}]'

I am using aws console to create deployment, with yaml appspec :

version: 0.0
Resources:
  - TargetService:
      Type: AWS::ECS::Service
      Properties:
        TaskDefinition: "arn:aws:ecs:eu-west-3:__AWS_ACCOUNT__:task-definition/deployment-app:9"
        LoadBalancerInfo:
          ContainerName: "app"
          ContainerPort: 80
        PlatformVersion: "LATEST"

Can anyone help me to understand my mistake ? Thanks !

Vincent
  • 733
  • 1
  • 9
  • 16
  • 1
    You left tons of info. Instead try to localize the issue yourself. You can try to replace problematic step with some standard one and see if it works. If it works, dive deeper into problematic step. If it doesn't work, something is wrong in the context. – nickolay.laptev Sep 15 '22 at 07:03
  • of course i'm still looking for on my side, i put all context because it can also be useful for those like me who didn't find a lot of resource on terraform+codedeploy – Vincent Sep 15 '22 at 07:36
  • I have 2 leads, first I changed route table and vpc table route, second I think permission is missing on my `ecsTaskExecutionRole`, still digging – Vincent Sep 15 '22 at 07:38

1 Answers1

0

I didn't know where to find a log from codeploy to know what was the problem. Finally, I just needed to go to the service, and check the provisionning task, after that the task failed with error message.

The problem came from my ecsTaskExecutionRole because it didn't has enought ECR rights to pull the image I built

Vincent
  • 733
  • 1
  • 9
  • 16