CI/CD — Force Redeploy AWS ECS Fargate For Same Docker Image NameTag With Terraform

Often wondered, how do you force ECS fargate to redeploy with your CI/CD pipeline when nothing changed in the terraform script other than the code rebuild into docker image with the same tag?

You certainly don’t want to see this in your CI/CD pipeline with terraform apply:

Apply complete! Resources: 0 added, 0 changed, 0 destroyed.

erraform AWS provider has this attribute force_new_deployment under aws_ecs_service, which if set to true should force redeploy with terraform apply.

resource "aws_ecs_service" "main" {
name = "my-app-service"
cluster = aws_ecs_cluster.main.id
task_definition = aws_ecs_task_definition.app.arn
desired_count = 2
launch_type = "FARGATE"
force_new_deployment = true

That attribute directly corresponds to the AWS ECS service flag.

Why should it work?

If you use AWS console (GUI), update service definition with Force new deployment set to true, it will reploy the tasks. AWS don’t store this flag as part of service definition. Next time, when you open update window of service definition again, you will see the flag is defaults to false.

If you use AWS CLI,

aws ecs update-service — cluster c — service s — force-new-deployment

Since Terraform works by state management, every apply should see the last set value of force_new_deployment of the service as false, and thus should attempt to update the service with true value.

ell at time of writing this, this is a known bug in Terraform provider for AWS:

https://github.com/hashicorp/terraform-provider-aws/issues/13528

WorkAround

To circumvent this bug, we will utilize the power of docker.

Docker generates new digest for every image
Each docker image can be uniquely identified by NAME[:TAG][@DIGEST]

So with little addition to our terraform script, we can cause task definition to change with every apply, and thus will trigger redeploy of the service.

  • When using AWS ECR as docker repository:
data "aws_ecr_image" "app" {
repository_name = "my-image-name"
image_tag = "my-image-tag"
}
#this will pull the latest digest for the given image from ECR
resource "aws_ecs_task_definition" "app" {
family = "my-app-service-task"
execution_role_arn = "my-ecs-task-execution-role"
network_mode = "awsvpc"
requires_compatibilities = ["FARGATE"]
cpu = 512
memory = 1024
container_definitions = <<DEFINITION
[
{
"name": "my-app-name",
"image": "my-image-name:my-image-tag@${data.aws_ecr_image.app.image_digest}",
...
#this inclusion of data image_digest will enforce task definition to change and thus service will redeploy.
  • When not using AWS ECR as docker repository:
#terraform script:
resource "aws_ecs_task_definition" "app" {
family = "my-app-service-task"
execution_role_arn = "my-ecs-task-execution-role"
network_mode = "awsvpc"
requires_compatibilities = ["FARGATE"]
cpu = 512
memory = 1024
container_definitions = <<DEFINITION
[
{
"name": "my-app-name",
"image": "my-image-name:my-image-tag@${var.image_digest}",
...
#Running in pipeline or script:#Extract the digest ID of the image in a variable
image_digest=$(docker images my-image-name:my-image-tag --format '{{.Digest}}')
#Pass it to TF var
TF_VAR_image_digest=$image_digest terraform apply

Downside

Until the known bug of Terraform is fixed, downside of using this approach is you get new task definition created with every deploy, if that concerns you.

you have any other ideas to circumvent the bug, I would love to hear those in comments.

I hope you liked ❤️ this article, stay tuned for more posts. All feedback, comments & questions are welcomed. 🏳️‍🌈

Donation😇

If this helped you reduce time to develop, you can buy me a cup of coffee ☕

Lead Full Stack & DevOps Engineer

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store