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
So found how to fix the problem. I was missing the following rights in the policy:
"ecr:GetAuthorizationToken",
"ecr:BatchCheckLayerAvailability",
"ecr:BatchGetImage",
"ecr:GetDownloadUrlForLayer"
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With