Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Terraform ECR STOPPED (CannotPullECRContainerError: AccessDeniedException)

I am trying to deploy a website container through Terraform. Everything goes right, just the task fails with

STOPPED (CannotPullECRContainerError: AccessDeniedException)

Here is a copy of my Terraform script:

    # Specify the provider and access details
    provider "aws" {
      region = "${var.aws_region}"
      access_key = "${var.access_key}"
      secret_key = "${var.secret_key}"
    }

    ## EC2

    ### Network

    data "aws_availability_zones" "available" {}

    resource "aws_vpc" "main" {
      cidr_block = "10.10.0.0/16"
    }

    resource "aws_subnet" "main" {
      count             = "${var.az_count}"
      cidr_block        = "${cidrsubnet(aws_vpc.main.cidr_block, 8, count.index)}"
      availability_zone = "${data.aws_availability_zones.available.names[count.index]}"
      vpc_id            = "${aws_vpc.main.id}"
    }

    resource "aws_internet_gateway" "gw" {
      vpc_id = "${aws_vpc.main.id}"
    }

    resource "aws_route_table" "r" {
      vpc_id = "${aws_vpc.main.id}"

      route {
        cidr_block = "0.0.0.0/0"
        gateway_id = "${aws_internet_gateway.gw.id}"
      }
    }

    resource "aws_route_table_association" "a" {
      count          = "${var.az_count}"
      subnet_id      = "${element(aws_subnet.main.*.id, count.index)}"
      route_table_id = "${aws_route_table.r.id}"
    }

    ### Compute

    resource "aws_autoscaling_group" "app" {
      name                 = "tf-website-asg"
      vpc_zone_identifier  = ["${aws_subnet.main.*.id}"]
      min_size             = "${var.asg_min}"
      max_size             = "${var.asg_max}"
      desired_capacity     = "${var.asg_desired}"
      launch_configuration = "${aws_launch_configuration.app.name}"
    }

    data "template_file" "cloud_config" {
      template = "${file("${path.module}/cloud-config.yml")}"

      vars {
        aws_region         = "${var.aws_region}"
        ecs_cluster_name   = "${aws_ecs_cluster.main.name}"
        ecs_log_level      = "info"
        ecs_agent_version  = "latest"
        ecs_log_group_name = "${aws_cloudwatch_log_group.ecs.name}"
      }
    }

    data "aws_ami" "stable_coreos" {
      most_recent = true

      filter {
        name   = "description"
        values = ["CoreOS Container Linux stable *"]
      }

      filter {
        name   = "architecture"
        values = ["x86_64"]
      }

      filter {
        name   = "virtualization-type"
        values = ["hvm"]
      }

      owners = ["595879546273"] # CoreOS
    }

    resource "aws_launch_configuration" "app" {
      security_groups = [
        "${aws_security_group.instance_sg.id}",
      ]

      key_name                    = "${var.key_name}"
      image_id                    = "${data.aws_ami.stable_coreos.id}"
      instance_type               = "${var.instance_type}"
      iam_instance_profile        = "${aws_iam_instance_profile.app.name}"
      user_data                   = "${data.template_file.cloud_config.rendered}"
      associate_public_ip_address = true

      lifecycle {
        create_before_destroy = true
      }
    }

    ### Security

    resource "aws_security_group" "lb_sg" {
      description = "controls access to the application ELB"

      vpc_id = "${aws_vpc.main.id}"
      name   = "tf-ecs-lbsg"

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

      egress {
        from_port = 0
        to_port   = 0
        protocol  = "-1"

        cidr_blocks = [
          "0.0.0.0/0",
        ]
      }
    }

    resource "aws_security_group" "instance_sg" {
      description = "controls direct access to application instances"
      vpc_id      = "${aws_vpc.main.id}"
      name        = "tf-ecs-instsg"

      ingress {
        protocol  = "tcp"
        from_port = 22
        to_port   = 22

        cidr_blocks = [
          "${var.admin_cidr_ingress}",
        ]
      }

      ingress {
        protocol  = "tcp"
        from_port = 80
        to_port   = 80

        security_groups = [
          "${aws_security_group.lb_sg.id}",
        ]
      }

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

    ## ECS

    resource "aws_ecs_cluster" "main" {
      name = "terraform_website_ecs_cluster"
    }

    data "template_file" "task_definition" {
      template = "${file("${path.module}/task-definition.json")}"

      vars {
        image_url        = "xxxxx.dkr.ecr.eu-west-1.amazonaws.com/nginx:latest"
        container_name   = "website"
        log_group_region = "${var.aws_region}"
        log_group_name   = "${aws_cloudwatch_log_group.app.name}"
      }
    }

    resource "aws_ecs_task_definition" "website" {
      family                = "tf_website_td"
      container_definitions = "${data.template_file.task_definition.rendered}"
    }

    resource "aws_ecs_service" "test" {
      name            = "tf-ecs-website"
      cluster         = "${aws_ecs_cluster.main.id}"
      task_definition = "${aws_ecs_task_definition.website.arn}"
      desired_count   = 1
      iam_role        = "${aws_iam_role.ecs_service.name}"

      load_balancer {
        target_group_arn = "${aws_alb_target_group.test.id}"
        container_name   = "website"
        container_port   = "80"
      }

      depends_on = [
        "aws_iam_role_policy.ecs_service",
        "aws_alb_listener.front_end",
      ]
    }

    ## IAM

    resource "aws_iam_role" "ecs_service" {
      name = "tf_website_ecs_role"

      assume_role_policy = <<EOF
    {
      "Version": "2008-10-17",
      "Statement": [
        {
          "Sid": "",
          "Effect": "Allow",
          "Principal": {
            "Service": "ecs.amazonaws.com"
          },
          "Action": "sts:AssumeRole"
        }
      ]
    }
    EOF
    }

    resource "aws_iam_role_policy" "ecs_service" {
      name = "tf_website_ecs_policy"
      role = "${aws_iam_role.ecs_service.name}"

      policy = <<EOF
    {
      "Version": "2012-10-17",
      "Statement": [
        {
          "Effect": "Allow",
          "Action": [
            "ec2:Describe*",
            "elasticloadbalancing:DeregisterInstancesFromLoadBalancer",
            "elasticloadbalancing:DeregisterTargets",
            "elasticloadbalancing:Describe*",
            "elasticloadbalancing:RegisterInstancesWithLoadBalancer",
            "elasticloadbalancing:RegisterTargets",
            "ecr:GetAuthorizationToken"
          ],
          "Resource": "*"
        }
      ]
    }
    EOF
    }

    resource "aws_iam_instance_profile" "app" {
      name = "tf-ecs-instprofile"
      role = "${aws_iam_role.app_instance.name}"
    }

    resource "aws_iam_role" "app_instance" {
      name = "tf-ecs-website-instance-role"

      assume_role_policy = <<EOF
    {
      "Version": "2012-10-17",
      "Statement": [
        {
          "Sid": "",
          "Effect": "Allow",
          "Principal": {
            "Service": "ec2.amazonaws.com"
          },
          "Action": "sts:AssumeRole"
        }
      ]
    }
    EOF
    }

    data "template_file" "instance_profile" {
      template = "${file("${path.module}/instance-profile-policy.json")}"

      vars {
        app_log_group_arn = "${aws_cloudwatch_log_group.app.arn}"
        ecs_log_group_arn = "${aws_cloudwatch_log_group.ecs.arn}"
      }
    }

    resource "aws_iam_role_policy" "instance" {
      name   = "TfEcsInstanceRole"
      role   = "${aws_iam_role.app_instance.name}"
      policy = "${data.template_file.instance_profile.rendered}"
    }

    ## ALB

    resource "aws_alb_target_group" "test" {
      name     = "tf-website-ecs-website"
      port     = 80
      protocol = "HTTP"
      vpc_id   = "${aws_vpc.main.id}"
    }

    resource "aws_alb" "main" {
      name            = "tf-website-alb-ecs"
      subnets         = ["${aws_subnet.main.*.id}"]
      security_groups = ["${aws_security_group.lb_sg.id}"]
    }

    resource "aws_alb_listener" "front_end" {
      load_balancer_arn = "${aws_alb.main.id}"
      port              = "80"
      protocol          = "HTTP"

      default_action {
        target_group_arn = "${aws_alb_target_group.test.id}"
        type             = "forward"
      }
    }

    ## CloudWatch Logs

    resource "aws_cloudwatch_log_group" "ecs" {
      name = "tf-ecs-group/ecs-agent"
    }

    resource "aws_cloudwatch_log_group" "app" {
      name = "tf-ecs-group/app-website"
    }

Thanks for the help

like image 585
Wanaoishi Avatar asked Jan 31 '18 11:01

Wanaoishi


2 Answers

So found how to fix the problem. I was missing the following rights in the policy:

"ecr:GetAuthorizationToken",
"ecr:BatchCheckLayerAvailability",
"ecr:BatchGetImage",
"ecr:GetDownloadUrlForLayer"
like image 170
Wanaoishi Avatar answered Nov 12 '22 05:11

Wanaoishi


I my case the default instance profile, called aws-elasticbeanstalk-ec2-role was missing the policy AmazonEC2ContainerRegistryFullAccess. Adding it solved the issue. aws-elasticbeanstalk-ec2-role

like image 34
Dejan S Avatar answered Nov 12 '22 04:11

Dejan S