Fargate in AWS ECS with Terraform

This post describes how to provision a container in AWS ECS from Terraform. The container’s image is fetched from docker hub. For the demonstration purposes, the nginx image will be used.

ECS service launch type described here is Fargate. This type gives simplicity.

The environment for provisioning with Terraform is a Docker container. More on that here. In order for this to work, AWS user credentials have to be generated as mentioned in the Administration section.

Administration

Create a user in AWS IAM and create access key for the user. Store ACCESS KEY and SECRET ACCESS KEY somewhere since they will be used in Terraform.

Add the following policies to the user:

  • AmazonVPCFullAccess
  • AmazonECS_FullAccess

You can fine-tune the policies as you wish, for the demo purpose this should be acceptable.

VPC

Preparing the VPC and security is a must, so the minimum in order to have the container running is described here.

This Terraform file creates a VPC, Internet Gateway, Route, Subnet and a Security Group which are alle needed to reach to the published container from the outside world. Fine-tuning of the VPC services is ignored for simplicity sake. Port 80 is opened to the world to be able to test the container.

ECS

Once the VPC is in place, the rest is quite simple. The ecs.tf shows how to get everything working.

Create cluster

Create the ECS cluster. This is launch type independent.

resource "aws_ecs_cluster" "ping" {
  name = "ping"

  setting {
    name  = "containerInsights"
    value = "enabled"
  }
}

Define task

Define how the container should look like: the resources needed, container image, ports,…

resource "aws_ecs_task_definition" "task" {
  family                        = "service"
  network_mode                  = "awsvpc"
  requires_compatibilities      = ["FARGATE", "EC2"]
  cpu                           = 512
  memory                        = 2048
  container_definitions         = jsonencode([
    {
      name      = "nginx-app"
      image     = "nginx:latest"
      cpu       = 512
      memory    = 2048
      essential = true  # if true and if fails, all other containers fail. Must have at least one essential
      portMappings = [
        {
          containerPort = 80
          hostPort      = 80
        }
      ]
    }
  ])
}

Argument container_definitions can also use Terraform function file. This makes the code easier to read. Here is an example of a Terraform file using the function, and here is the JSON file the function uses as the argument.

Service

Now we can finally deploy the service – create the container and use it

resource "aws_ecs_service" "service" {
  name              = "service"
  cluster           = aws_ecs_cluster.ping.id
  task_definition   = aws_ecs_task_definition.task.id
  desired_count     = 1
  launch_type       = "FARGATE"
  platform_version  = "LATEST"

  network_configuration {
    assign_public_ip  = true
    security_groups   = [aws_security_group.sg.id]
    subnets           = [aws_subnet.subnet.id]
  }
  lifecycle {
    ignore_changes = [task_definition]
  }
}

The service is attached to a specific cluster and specific task definition. The launch type is FARGATE. Public IP will be assigned and the service will be in a specific subnet and secured by a specific security group.

Once all is provisioned we can check the result:

Go into AWS Console and find service ECS. Make sure you are in the right region. Click on clusters and find the cluster and click on it. Under tasks you should se the provisioned container, something similar to this:

Clicking on the task ID should give you task details. Under Network is the public IP. Copy it and visit it. Nginx should welcome you.