terraform-provider-aws: Inconsistent order of environment variables in aws_ecs_task_definition
Folks,
It seems that order of environment variables is not being preserved. Also, mountPoints and volumesFrom are being showed in the plan, even when they’re not defined in TF.
Large number of moving parts like environmental variables make working with big task definitions rather unpleasant 😃
Terraform Version
Terraform v0.11.2
- provider.aws v1.7.0
Affected Resource(s)
aws_ecs_task_definition
Steps to Reproduce
resource "aws_ecs_task_definition" "service2" {
family = "service"
container_definitions = "${file("service.json")}"
}
service.json:
[{
"cpu": 10,
"environment": [
{
"name": "APP_VERSION",
"value": "dev-26758"
},
{
"name": "DATADOG_ENV",
"value": "ee1-llll-pppppppp"
},
{
"name": "DATADOG_PATCH_MODULES",
"value": "celery:true,elasticsearch:true,flask:true,httplib:true,mysql:true,redis:true,requests:true"
},
{
"name": "DATADOG_SERVICE_NAME",
"value": "translation-service"
},
{
"name": "DATADOG_TRACE_AGENT_HOSTNAME",
"value": "172.17.0.1"
},
{
"name": "DATADOG_TRACE_AGENT_PORT",
"value": "8126"
},
{
"name": "DATADOG_TRACE_ENABLED",
"value": "false"
}
],
"essential": true,
"image": "wordpress1",
"memory": 500,
"name": "wordpress",
"portMappings": [{
"containerPort": 80,
"hostPort": 0,
"protocol": "tcp"
},
{
"containerPort": 81,
"hostPort": 0,
"protocol": "tcp"
},
{
"containerPort": 82,
"hostPort": 0,
"protocol": "tcp"
}
]
}]
If I bump APP_VERSION from dev-26758 to dev-26760 I get:
-/+ aws_ecs_task_definition.service2 (new resource required)
id: "service" => <computed> (forces new resource)
arn: "arn:aws:ecs:eu-west-1:000000000000:task-definition/service:14" => <computed>
container_definitions: "[{\"cpu\":10,\"environment\":[{\"name\":\"DATADOG_ENV\",\"value\":\"ee1-llll-pppppppp\"},{\"name\":\"DATADOG_TRACE_AGENT_HOSTNAME\",\"value\":\"172.17.0.1\"},{\"name\":\"DATADOG_SERVICE_NAME\",\"value\":\"translation-service\"},{\"name\":\"DATADOG_PATCH_MODULES\",\"value\":\"celery:true,elasticsearch:true,flask:true,httplib:true,mysql:true,redis:true,requests:true\"},{\"name\":\"DATADOG_TRACE_AGENT_PORT\",\"value\":\"8126\"},{\"name\":\"DATADOG_TRACE_ENABLED\",\"value\":\"false\"},{\"name\":\"APP_VERSION\",\"value\":\"dev-26758\"}],\"essential\":true,\"image\":\"wordpress1\",\"memory\":500,\"mountPoints\":[],\"name\":\"wordpress\",\"portMappings\":[{\"containerPort\":80,\"hostPort\":0,\"protocol\":\"tcp\"},{\"containerPort\":81,\"hostPort\":0,\"protocol\":\"tcp\"},{\"containerPort\":82,\"hostPort\":0,\"protocol\":\"tcp\"}],\"volumesFrom\":[]}]" => "[{\"cpu\":10,\"environment\":[{\"name\":\"APP_VERSION\",\"value\":\"dev-26760\"},{\"name\":\"DATADOG_ENV\",\"value\":\"ee1-llll-pppppppp\"},{\"name\":\"DATADOG_PATCH_MODULES\",\"value\":\"celery:true,elasticsearch:true,flask:true,httplib:true,mysql:true,redis:true,requests:true\"},{\"name\":\"DATADOG_SERVICE_NAME\",\"value\":\"translation-service\"},{\"name\":\"DATADOG_TRACE_AGENT_HOSTNAME\",\"value\":\"172.17.0.1\"},{\"name\":\"DATADOG_TRACE_AGENT_PORT\",\"value\":\"8126\"},{\"name\":\"DATADOG_TRACE_ENABLED\",\"value\":\"false\"}],\"essential\":true,\"image\":\"wordpress1\",\"memory\":500,\"name\":\"wordpress\",\"portMappings\":[{\"containerPort\":80,\"hostPort\":0,\"protocol\":\"tcp\"},{\"containerPort\":81,\"hostPort\":0,\"protocol\":\"tcp\"},{\"containerPort\":82,\"hostPort\":0,\"protocol\":\"tcp\"}]}]" (forces new resource)
family: "service" => "service"
network_mode: "" => <computed>
revision: "14" => <computed>
or in more human friendly form:
-/+ aws_ecs_task_definition.service2 (new resource required)
id: "service" => "<computed>" (forces new resource)
arn: "arn:aws:ecs:eu-west-1:000000000000:task-definition/service:14" => "<computed>"
container_definitions: [
{
"cpu": 10,
"environment": [
{
+ "name": "APP_VERSION",
+ "value": "dev-26760"
+ },
+ {
"name": "DATADOG_ENV",
"value": "ee1-llll-pppppppp"
},
{
- "name": "DATADOG_TRACE_AGENT_HOSTNAME",
- "value": "172.17.0.1"
+ "name": "DATADOG_PATCH_MODULES",
+ "value": "celery:true,elasticsearch:true,flask:true,httplib:true,mysql:true,redis:true,requests:true"
},
{
"name": "DATADOG_SERVICE_NAME",
"value": "translation-service"
},
{
- "name": "DATADOG_PATCH_MODULES",
- "value": "celery:true,elasticsearch:true,flask:true,httplib:true,mysql:true,redis:true,requests:true"
+ "name": "DATADOG_TRACE_AGENT_HOSTNAME",
+ "value": "172.17.0.1"
},
{
"name": "DATADOG_TRACE_AGENT_PORT",
"value": "8126"
},
{
"name": "DATADOG_TRACE_ENABLED",
"value": "false"
- },
- {
- "name": "APP_VERSION",
- "value": "dev-26758"
}
],
"essential": true,
"image": "wordpress1",
"memory": 500,
- "mountPoints": [
-
- ],
"name": "wordpress",
"portMappings": [
{
"containerPort": 80,
"hostPort": 0,
{
"containerPort": 82,
"hostPort": 0,
"protocol": "tcp"
}
- ],
- "volumesFrom": [
-
]
}
] (forces new resource)
network_mode: "" => "<computed>"
revision: "14" => "<computed>"
About this issue
- Original URL
- State: closed
- Created 6 years ago
- Reactions: 41
- Comments: 25 (6 by maintainers)
@piotrb yours is the only discussion I found of this problem. Just so you know, https://github.com/terraform-providers/terraform-provider-aws/pull/11463 should fix that.
This seems to have generally improved in the latest versions of the provider and TF itself … but some improvements would be great …
It doesn’t seem like the ordering causes the task definitions to need to be updated any more …
But when there is changes the diff is very nonsensical since it seems to be diffing against the raw order.
Could the provider not manage the order of the fields as they’re coming from aws and when they’re persisted to state? This would then just make the diff alphabetical and make the whole thing much more pleasant … right now its a pain to actually analyze the changes in the variables …
What was my actual change here? NONE … I didn’t add any env … I made other changes on the task definition …
It’s far from ideal but with the help of terraform 0.12 you can re-order the environment variables to match the order given by AWS.
@tamsky is correct. However, for clarity, this should still be considered a bug since it renders a confusing diff - as evidenced by this thread. 😃
Can we please have an update, we are having lots of issues with this bug @s-maj
@nhooey In my last comment, I tried to explain that the order of
ENVIRONMENTelements interraform planoutput is misleading.The
aws_provideris 100% properly canonicalizing all ENVIRONMENT variable lists before calculating diffs. I even link to the code where that happens.At the same time,
terraform planoutput is not and does not canonicalize that struct before emitting it, giving an appearance of being the source of a diff, when, if only order is different, it is not a diff.It is my strong opinion that there is something else in your ECS task resource’s definition that is different.
Have you checked the provider’s output under
TF_LOG=DEBUG?So it looks like there’s also issues with volumes too:
However, if there are no other changes the volumes don’t show changes either. It appears that if there are other changes then the volumes get added to the diff also. i.e. if the
container_definitionshave changes, then so does volumes.Hello,
Just wondering if there’s any update on this? This is unfortunately causing our ECS Task Definition to be replaced every time despite zero changes.
The ordering once it gets to AWS certainly seems very random!
It’s possible to work around this by making sure your local task definition has the same order as stored on Amazon, which can be queried using the AWS cli (or just copy, paste, and sanitise the json that Terraform returns when planning). The order will remain the same until you add a new variable key-pair. Annoying, but it’s the only way to avoid your plans getting junked.
Improvements to this difference handling have been merged and will release with version 2.68.0 of the Terraform AWS Provider, later this week. Thanks to @jbergknoff-rival for the implementation. 👍
Is there a plan to add a feature that lets the user say “Ignore the order of JSON lists for this particular JSON field value”, so the AWS
ENVIRONMENTJSON list doesn’t get detected as a diff when AWS chooses a stupid, arbitrary order that is different from what was submitted by Terraform?In my case, the ECS tasks are getting recreated every time because AWS re-orders JSON lists unpredictably, but the reordering is static.
It’s hard to see what the course of action is from this ticket.
Suggestion:
Use the DEBUG output from the provider to determine the actual source of your diffs.
tl;dr summary:
Inconsistent order between
environmentobjects is highly unlikely to be the cause of resource-level diffs.diffs shown in
planoutput are not canonicalized. Therefore, there can be visual differences in their element ordering.my problem/fix summary:
I lacked default values for my
healthcheck:s. Adding the following values resolved my persistent diffs issue:I’d suggest these missing values should not have caused diffs. Perhaps there’s an existing feature request? If someone knows of one, please mention/link here.
Debug journal:
terraform:
v0.11.14withplugin.terraform-provider-aws_v2.20.0_x4:.My first guess was that I had diffs because the value of this field is an opaque JSON string, and differences in object sort returned by the AWS API would be the cause of diffs.
Turns out, there’s an entire go file dedicated to comparing ECS task definitions:
That looks good, and the tests look good; it appears to effectively test that un-ordered
environmentobjects compare as “equal to” orderedenvironmentobjects.The tested function is actually being called, so it’s active code:
I’m now convinced object order is not my problem.
Now I want to see the canonicalized JSON and check it for diffs.
Checking TF_LOG=DEBUG output, I was able to see the canonicalized
First:andSecond:DEBUG values and was able to compare them for actual diffs.No surprise, there were actual diffs.
My issue was due to not including any values for a few
healthcheck:defaults which the AWS API happily inserts.There is nice a nice function https://github.com/terraform-providers/terraform-provider-aws/blob/fae04dfedfbd653d6a0bdbcc5d7c04f3d54e3048/aws/ecs_task_definition_equivalency.go#L56 used to reorder (diff suppression) task definition during diff but it’s used only there. All values go raw to the state https://github.com/terraform-providers/terraform-provider-aws/blob/fae04dfedfbd653d6a0bdbcc5d7c04f3d54e3048/aws/resource_aws_ecs_task_definition.go#L265 https://github.com/terraform-providers/terraform-provider-aws/blob/401dc017401659015fa0afdde5336b891c695fe6/aws/structure.go#L621
@radeksimko added task definition migration from sha to json (thanks million for that) plus further fixes. I was wondering if you could help us again to get this fixed 😃