Skip to content

Instantly share code, notes, and snippets.

@codyhazelwood
Forked from damscott/ecs-service.tf
Created May 19, 2022 21:56
Show Gist options
  • Save codyhazelwood/462b526a21ea1962c76a1285001dba9c to your computer and use it in GitHub Desktop.
Save codyhazelwood/462b526a21ea1962c76a1285001dba9c to your computer and use it in GitHub Desktop.
Using CI/CD for deployment of ECS task definitions and images causes drift between the tfstate and AWS. Using a bash script in a Terraform External Data Source to pull the current task definition revision and container image tag from AWS keeps the state in sync with changes from CI. This does not currently support multiple containers in a single…
resource "aws_ecs_service" "service" {
name = "${var.app}"
cluster = "${var.cluster}"
# Use task revision in AWS if it is greater than task revision in tfstate
# Prevents rolling back revision when it has been incremented by CI
task_definition = "${aws_ecs_task_definition.app.family}:${data.external.task_definition.result["task_definition_revision"] > aws_ecs_task_definition.app.revision ? data.external.task_definition.result["task_definition_revision"] : aws_ecs_task_definition.app.revision }"
desired_count = "${var.task_count}"
depends_on = [
"aws_ecs_task_definition.app"
]
}
resource "aws_ecs_task_definition" "task" {
family = "${var.app}-${var.env}"
task_role_arn = "${aws_iam_role.app_role.arn}"
container_definitions = <<JSON
[
{
"name": "${var.app}",
"image": "${aws_ecr_repository.app_repo.repository_url}:${data.external.task_definition.result["image_tag"]}"
}
]
JSON
}
data "external" "task_definition" {
program = ["bash", "${path.module}/ecs-task-definition.sh"]
query = {
service = "${var.app}"
cluster = "${var.cluster}"
path_root = "${jsonencode(path.root)}"
}
}
#!/bin/bash
# This script retrieves the container image and task definition revision
# for a given cluster+service. If it can't retrieve it, assume
# this is the initial deployment and default to "latest".
defaultImageTag='latest'
# Exit if any of the intermediate steps fail
set -e
# Get parameters from stdin
eval "$(jq -r '@sh "service=\(.service) cluster=\(.cluster) path_root=\(.path_root)"')"
# Remove extra quotes and backslashes from jsonencoding path_root in terraform
path_root="$(echo $path_root | sed -e 's/^"//' -e 's/"$//' -e 's/\\\\/\\/g')"
taskDefinitionID="$(aws ecs describe-services --service $service --cluster $cluster | jq -r .services[0].taskDefinition)"
# If a task definition is already running in AWS, use the revision and image tag from it
if [[ ! -z "$taskDefinitionID" && "$taskDefinitionID" != "null" ]]; then {
taskDefinitionRevision="$(echo "$taskDefinitionID" | sed 's/^.*://')"
taskDefinition="$(aws ecs describe-task-definition --task-definition $taskDefinitionID)"
containerImage="$(echo "$taskDefinition" | jq -r .taskDefinition.containerDefinitions[0].image)"
imageTag="$(echo "$containerImage" | awk -F':' '{print $2}')"
# Default to "latest" if taskDefinition doesn't exist
# Set revision to 0 so terraform logic uses task definition from terraform
} else {
imageTag=$defaultImageTag
taskDefinitionRevision='0'
}
fi
# Generate a JSON object containing the image tag.
jq -n --arg imageTag $imageTag --arg taskDefinitionRevision $taskDefinitionRevision '{image_tag: $imageTag, task_definition_revision: $taskDefinitionRevision}'
exit 0
Display the source blob
Display the rendered blob
Raw
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment