-
Notifications
You must be signed in to change notification settings - Fork 9.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Keep LATEST aws_ecs_task_definition container_definition image revision #20121
Comments
there's a old issue for this #632 the solution could be:
using |
We're using an SSM parameter for this. The resource sets up an initial value (eg. latest) when created, but value changes to this parameter are then ignored. |
This is a great solution. thank you. |
I'm trying to set up an ECS deployment pipeline and running into similar issues. IIUC the workarounds so far, and from #632, with the exception of @WhyNotHugo's template idea, all would still create a diff in the task definition, because The change from @GerardSoleCa in #30154 feels like a really great solution to this. It looks like you'd be able to do something like this: locals {
task_definition_family = var.service_name
container_name = var.service_name
is_lookup = var.image_tag == null
}
// If a var.image_tag is passed in, use it for first-run
// If a var.image_tag is not passed in, look up the deployed container definition
data "aws_ecs_service" "service" {
count = local.is_lookup ? 1 : 0
cluster_arn = var.cluster_arn
service_name = var.service_name
}
data "aws_ecs_container_definition" "container" {
count = local.is_lookup ? 1 : 0
task_definition = data.aws_ecs_service.service[0].task_definition
container_name = local.container_name
}
locals {
deployed_image_tag = local.is_lookup ? try(split(":", data.aws_ecs_container_definition.container[0].image)[1], null) : null
wanted_image_tag = coalesce(var.image_tag, local.deployed_image_tag)
max_task_def_revision = max(aws_ecs_task_definition.ecs_task[0].revision, data.aws_ecs_task_definition.ecs_task[0].revision)
}
// Get the existing task revision, need to depend on the task.
data "aws_ecs_task_definition" "ecs_task" {
task_definition = aws_ecs_task_definition.ecs_task.arn_without_revision
depends_on = [
aws_ecs_task_definition.ecs_task
]
}
resource "aws_ecs_task_definition" "ecs_task" {
family = "${var.application_name}-${var.environment}"
track_latest = true // new from the above PR
// ... omitted for brevity ...
container_definitions = jsonencode(
[
{
name = local.container_name
image = "${var.image_repo}:${var.image_tag}"
}
]
)
}
resource "aws_ecs_service" "ecs_service" {
name = var.service_name
cluster = var.cluster_arn
task_definition = "${local.task_definition_family}:${local.max_task_def_revision}"
} This provides a path for creating the service on first run. Then on subsequent external CI changes to the image_tag, terraform would be able to pick up the image. Since the task definition is now tracking LATEST, instead of the original revision it created, terraform would not detect a difference. If changes were made to environment variables or other terraform code, terraform would be able to fetch the deployed image. Without #30154, CI deploys would cause Terraform to detect a diff, and create a new task definition that matches the currently active one. It doesn't cause issues to the service but would trigger a deploy and produce a long diff. |
Use the aws_ecs_task_definition data source to re-construct the task definition ARN like this But you'll need to synchronize the changes made by the two parties (Terraform and whatever else is making task def revisions) - usually this is the image tag which you can store in an SSM parameter. So if you deploy your service like this, Terraform creates a new task def at rev 0 Then, your CI process kicks off and creates a new image version, updates the SSM parameter then creates a new task def version and deploys the changes. If you re-run Terraform, without task def changes - no changes are detected for the task def since its pulling the latest task def arn from the data source and parsing trick If you re-run Terraform, with changes to the task definition - it will create a new task def revision but USING the image tag set in the SSM parameter Its not ideal, but it works. One other fault - you'll need to supply a placeholder image on initial deploy since your pipeline may not have created an image yet (i.e. - no value in the SSM parameter on first deployment) |
Oh hey @bryantbiggs, I was actually just opening an issue in https://github.com/terraform-aws-modules/terraform-aws-ecs to ask you about this. I've read your design doc multiple times now 😅 I don't quite understand this part:
and similarly from your doc
I'm parsing the image by fetching the actively deployed task definition from IIUC, with the SSM approach, the SSM parameter is updated on deploy. Since your container definition is constructed using this SSM parameter, re-running terraform would construct a new container definition with the updated SSM parameter. This would cause Or a hypothetical sequence of events:
Am I misunderstanding something here? |
Hi all, There is one important thing to state @bryantbiggs, the created In the case you remove old revisions (sometimes you don't want to keep old stuff there), the A second issue I might see not using the latest one, is that you check only against container changes for example, but the revision that the resource 'considers/handles' is much older than the task definition you have running in production. And finally, with the module you are showing, don't you have the chicken-egg issue when starting from 0? I see some kind of circular dependency between the I'll try to put a couple of examples: A. Deleting old Task Definitions
B. Keeping Task Definitions
And the worst case scenario is that on step 5, we would be updating with an old docker image if we are not synchronising properly the docker image from step 4 and step 5. So, in any case. The approach I'm trying to cover in this PullRequest would solve both issues. We could track always the latest active Task Definition. Cheers! |
👋 @GerardSoleCa We're excited to see #30154 and appreciate you making the change! Is there anything preventing it from being merged? |
Still waiting that someone from Hashicorp jumps in and reviews the code. Maybe also helping me writing or guiding me through the unit testing of that part. If you can upvote the PR I'd appreciate!! |
PR has just been approved ! lol |
This functionality has been released in v5.37.0 of the Terraform AWS Provider. |
Hi @GerardSoleCa, Could you please take a look at the example terraform-aws-modules/terraform-aws-ecs#171 (comment)? |
This introduced a bug in the
Seems like since I was renaming an ALB that it should not have stopped the move, especially since I was not on provider 5.37. |
Is it possible that someone else with access to your state is using 5.37 and updated it? Do you commit your .terraform.lock.hcl file?
|
nope, I was the only one operating on that repo at that time. |
In my opinion, having a separate Terraform resource like An appropriate issue - #17988 Or use a hack with the |
I think the recently merged #30154 does solve the problem. Here's how it works for me: track_latest = true
image = data.aws_ecs_container_definition.this.image Terraform is now smart enough to compare the resource to the latest task revision, even if it was deployed outside terraform, i.e. even if it is not tracked by terraform state yet. So, I think this issue can be closed. 🤷♀️ |
Btw, for curiosity, another workaround I was employing for this was to run: terraform apply -refresh-only Terraform was smart enough to only update task definition revision in its state, and so subsequent |
To make it work on our side we neede to change 2 things: resource "aws_ecs_task_definition" "task_definition" {
track_latest = true
} and also to use the ecr_repository resource to select the latest tag pushed: data "aws_ecr_repository" "ecr" {
name = "ecr-name"
registry_id = "registry-id-if-needed"
}
locals {
image_tag = coalesce(setsubtract(data.aws_ecr_repository.ecr.most_recent_image_tags, ["latest"])...)
} and use the |
Finally I had some time to add this to my configs. But I just need this, the rest is working properly in my case. No more recreations, we only see the updates in place. Also, diffs are somehow better. |
pls show working code example for aws_ecs_service and aws_ecs_task_definition[container_definitions] with track_latest = true. |
@alexgoddity here is an example based on the sample locals {
registry_id = "1234567890"
# Most recent image is pushed with 2 tags: `latest` and the `git-sha1` value, and we want to use the `git-sha1` to be explicit
image = "${data.aws_ecr.repository.repository_url}:${coalesce(setsubtract(data.aws_ecr_repository.opa_snapshot.most_recent_image_tags, ["latest"])...)}"
# If there's only a single tag pushed, it can be simpler
# image = "${data.aws_ecr.repository.repository_url}:${coalesce(data.aws_ecr_repository.opa_snapshot.most_recent_image_tags)}"
}
data "aws_ecr_repository" "ecr" {
name = "ecr-name"
registry_id = local.registry_id
}
resource "aws_ecs_task_definition" "service" {
family = "service"
track_latest = true
container_definitions = jsonencode([
{
name = "first"
image = local.image
cpu = 10
memory = 512
essential = true
portMappings = [
{
containerPort = 80
hostPort = 80
}
]
},
{
name = "second"
image = "service-second"
cpu = 10
memory = 256
essential = true
portMappings = [
{
containerPort = 443
hostPort = 443
}
]
}
])
volume {
name = "service-storage"
host_path = "/ecs/service-storage"
}
placement_constraints {
type = "memberOf"
expression = "attribute:ecs.availability-zone in [us-west-2a, us-west-2b]"
}
} |
Thanks I am also looking for a solution to manage tasks from multiple sources. |
We are using https://github.com/silinternational/ecs-deploy in the CI which creates a new task on CI iterations based on the last one. |
I managed to solve the same problem with the help of resource "aws_ssm_parameter" "image_tag" {
name = "image-tag-name"
type = "String"
value = "latest"
lifecycle {
ignore_changes = [value]
}
}
data "aws_ssm_parameter" "image_tag" {
name = "image-tag-name"
depends_on = [
aws_ssm_parameter.image_tag
]
}
data "aws_ecr_repository" "ecr" {
name = "ecr-name"
}
resource "aws_ecs_task_definition" "app_task" {
family = "task-family"
track_latest = true
container_definitions = <<DEFINITION
[
{
"name": "app-name",
"image": "${data.aws_ecr.repository.repository_url}:${data.aws_ssm_parameter.image_tag.value}",
}
]
DEFINITION
...
} To deploy the application, I leverage GitHub Actions to update both the task definition ( |
I don't understand how I deployed via terraform, and then altered my image name outside of terraform. I assumed So it made no difference, so am I misunderstanding how this is supposed to work? |
What track_latest is doing is on the plan fetch the latest task definition found in AWS, not using the version defined in the tfstate. Then using the latest version in the AWS compares with what you have as code in your TF. So it won't autoupdate the image for you, that is one thing you need to do. Some of us we fetch the image with a data or a script, others fetch the docker image tag using ssm parameters. What we get with this track_latest, is that no new task definition is created everytime only when there are updates. But it's up to you to sync the docker image tag. Cheers! |
With However, I do get on diff on
but that doesn't actually matter as such. It is annoying but it doesn't alter anything in AWS when I apply. If I instead specify |
What I did was
There was no need for |
Community Note
Description
I'd like to keep a reference to the latest image in use for the task definition (revision) when changing other container definitions.
My deployed images on ECR uses a git commit SHA to tag them (like image-name:72937423940das44).
A single image is deployed on a service for staging, and after approval, to the production ECS service.
The issue I'm facing is that, when I make changes to the infrastructure, it loses the reference to the current deployed image revision, so I have to change the infrastructure, then re-run the latest deployment on CI to update to the latest image revision.
I did not find any ways to get the current image revision and keep it on "container_definitions -> image" field, and just apply the change on other fields.
If there was a datasource that could retrieve the latest revision from the current task definition I could manually check for it on the image field and use the default ECR url otherwise.
I've tried with
aws_ecs_task_definition
datasource, but it only outputs the revision, and theaws_ecs_container_definition
requires the id of the task. Tried other workarounds to set the image to the current used one, but it ends in circular dependency.New or Affected Resource(s)
Potential Terraform Configuration
References
The text was updated successfully, but these errors were encountered: