1
votes

Happy Friday! hoping someone can help me with this issue or point out the flaws in my thinking.

$ terraform --version
Terraform v0.12.7
+ provider.aws v2.25.0
+ provider.template v2.1.2

Preface

This is my first time using Terraform. We have an existing AWS ECS/Fargate environment up and running, this is in a 'test' environment. We recently (e.g. after setting up the test env) started to use Terraform for IaC purposes.

Current Config

The environment has a single ECS cluster, we're using FARGATE but I'm not sure that matters for this question. The cluster has several services, each service has a single task (docker image) associated with it - so they can be individually scaled. Each docker image has its own Repo.

What I'm trying to do

So with Terraform I was hoping to be able to create, update and destroy the environment. Creating/destroying seems fairly straight forward, however; I'm hitting a road-block for updating.

As I said each task has its own repo, when a pull request is made against the repo our CI platform (CircleCI if that matters) builds the new docker image, tags it and pushes it. Then we use an API call to trigger a build of the Terraform Repo passing the name of the service/task that was updated.

Problem

The problem we're facing is that when going through the services (described below) I can't figure out how to get Terraform to either ignore the services that are not being updated, or how I can provide the correct container_definitions in the aws_ecs_task_definition, specifically the current image tag (we don't use the latest tag). So I'm trying to figure out how I can get the latest container information (tag) or just tell Terraform to skip the unmodified task.

Terraform Script

Here is a stripped down version of what I have tried, this is in a module called ecs.tf, the var.ecs_svc_names is a list of the service names. I have removed some elements as I don't think they pertain to this issue and having them makes this very large.

CAVEATS I have not run the Terraform 'script' as shown below due to the issues I am asking about, so my syntax maybe a off. Sorry if that is the case, hopefully this will show you what I'm trying to do....

ecs.tf

/* ecs_service_names is passed in, but here is its definition:
variable "ecs_service_names" {
  type = list(string)
  description = "This is a list/array of the images/services that are contained in the cluster"
  default = [
    "main",
    "sub-service1",
    "sub-service2"]
}
*/

locals {
  numberOfServices = length(var.ecs_svc_names)
}

resource "aws_ecs_cluster" "ecs_cluster" {
  name = "${var.env_type}-ecs-cluster"
}

// Create the service objects (1 for each service)
resource "aws_ecs_service" "ecs-service" {
  // How many services are being created
  count = local.numberOfServices
  name = var.ecs_svc_names[count.index]
  cluster = aws_ecs_cluster.ecs_cluster.id
 definition[count.index].family}:${max(aws_ecs_task_definition.ecs-task-definition[count.index].revision, data.aws_ecs_task_definition.ecs-task-def.revision)}"
  desired_count = 1
  launch_type = "FARGATE"
// stuff removed
}

resource "aws_ecs_task_definition" "ecs-task-definition" {
  // How many tasks. There is a 1-1 relationship between tasks and services
  count = local.numberOfServices
  family = var.ecs_svc_names[count.index]
  network_mode = "awsvpc"
  requires_compatibilities = ["FARGATE"]
// cpu/memory stuff removed
  task_role_arn = var.ecs_task_role_arn

  container_definitions = data.template_file.ecs_containers_json[count.index].rendered
}

data.tf

locals {
    numberOfServices = length(var.ecs_svc_names)
}
data "aws_ecs_task_definition" "ecs-task-def" {
    // How many services are being created, 1-1 relationship between tasks and services
    count = local.numberOfServices
    task_definition = aws_ecs_task_definition.ecs-task-definition[count.index].family

    depends_on = [
        "aws_ecs_task_definition.ecs-task-definition",
    ]
}

data "template_file" "ecs_containers_json" {
    // How many tasks. There is a 1-1 relationship between tasks and services
    count = local.numberOfServices
  template = file("${path.module}/container.json.template")
  vars = {
// vars removed

      image = aws_ecs_task_definition.ecs-task-definition[count.index].family

// This is where I hit the road-block, how do I get the current docker tag from Terraform?
      tag = var.ecs_svc_name_2_update == var.ecs_svc_names[count.index]
            ? var.ecs_svc_image_tag
            : data.aws_ecs_task_definition.ecs-task-def[count.index].
}

I didn't post the JSON document, if you need that I can provide it...

Thank you

1

1 Answers

0
votes

It is necessary to pass the updated image attribute in the container definition of the task definition revision.

You can data source the container definition of the current task revision which is used by the service and pass it to the terraform. You may follow the code below.

data "template_file" "example" {
  template = "${file("${path.module}/example.json")}"

  vars {
    image = "${data.aws_ecs_container_definition.example.image}"
  }
}

resource "aws_ecs_task_definition" "example" {
  family                = "${var.project_name}-${var.environment_name}-example"
  container_definitions = "${data.template_file.example.rendered}"
  cpu                   = 192
  memory                = 512
}

data "aws_ecs_container_definition" "example" {
  task_definition = "${var.project_name}-${var.environment_name}-example"
  container_name  = "example"
}