ordered_placement_strategy uses map instead of list, breaking strategy order
Description
The ordered_placement_strategy variable in the ECS service module is defined as a map(object({...})) type, which is problematic because maps are not ordered collections in Terraform. According to AWS ECS documentation, placement strategies are evaluated in order from top to bottom, and the ordering is significant for determining how tasks are placed.
Using a map for this variable means that:
- The order of placement strategies cannot be guaranteed
- This contradicts the variable's own description which states "List from top to bottom in order of precedence"
- Users cannot reliably control the order in which placement strategies are applied
- [x] I have searched the open/closed issues and my issue is not listed.
Versions
- Module version: Latest (main branch)
- Terraform version: N/A - This is a module design issue
- Provider version(s): N/A - This is a module design issue
Reproduction Code
The issue exists in the module variable definitions:
Root module (variables.tf:321-324):
ordered_placement_strategy = optional(map(object({
field = optional(string)
type = string
})))
Service module (modules/service/variables.tf:218-225):
variable "ordered_placement_strategy" {
description = "Service level strategy rules that are taken into consideration during task placement. List from top to bottom in order of precedence"
type = map(object({
field = optional(string)
type = string
}))
default = null
}
Service module usage (modules/service/main.tf:153-160):
dynamic "ordered_placement_strategy" {
for_each = var.ordered_placement_strategy != null ? var.ordered_placement_strategy : {}
content {
field = ordered_placement_strategy.value.field
type = ordered_placement_strategy.value.type
}
}
Example demonstrating the issue
When using this Terraform configuration:
ordered_placement_strategy = {
spread = {
type = "spread"
field = "attribute:ecs.availability-zone"
},
binpack = {
type = "binpack"
field = "memory"
}
}
The resulting task placement strategy in AWS is:
binpack (MEMORY), spread (attribute:ecs.availability-zone)
Note how the order is reversed from what was specified in the Terraform code. This is because maps in Terraform do not preserve insertion order, and the keys are sorted alphabetically (binpack comes before spread alphabetically), leading to unpredictable and incorrect task placement behavior.
Expected behavior
The ordered_placement_strategy variable should be defined as a list(object({...})) to preserve the order of placement strategies. For example:
variable "ordered_placement_strategy" {
description = "Service level strategy rules that are taken into consideration during task placement. List from top to bottom in order of precedence"
type = list(object({
field = optional(string)
type = string
}))
default = null
}
Hello @jworks-mwb
Interested in making a fix here. Can make the proposed changes
variable "ordered_placement_strategy" {
description = "Service level strategy rules that are taken into consideration during task placement. List from top to bottom in order of precedence"
# CHANGED from map to list
type = list(object({
field = optional(string)
type = string
}))
# Changed default from null to empty list for consistency with list type
default = []
}
AND updating the dynamic block in modules/service/main.tf to iterate over the list using the index:
dynamic "placement_strategy" {
for_each = var.ordered_placement_strategy
content {
field = placement_strategy.value.field
type = placement_strategy.value.type
}
}
Let me know what you think
How does this affect users, does this break people that are using a map? I'm not too familiar with how terraform handles type mismatches.
What I have done to get around this was ensuring the keys sort lexicographically, something like:
ordered_placement_strategy = {
1 = {
type = "spread"
field = "attribute:ecs.availability-zone"
},
2 = {
type = "binpack"
field = "memory"
}
}
@jworks-mwb You can see the working fix in my branch here: https://github.com/lukzhang/terraform-aws-ecs/tree/fix/ecs-ordered-strategy
I changed the data type for ordered_placement_strategy in modules/service/variables.tf from a map to a list of objects. This forces Terraform to preserve the order you define in your configuration.
I verified the terraform plan output confirms the correct ordering. Please let me know what you think. This is a snippet of the terraform.tfvars file. I verified this works locally by providing my own VPC ID and Subnets, and the terraform plan output confirms the correct ordering.
Input Configuration Example (.tfvars snippet):
ordered_placement_strategy = [
{
type = "spread"
field = "attribute:ecs.availability-zone"
},
{
type = "binpack"
field = "memory"
}
]
To avoid breaking the change, not sure if it would be better to add a new variable to use list rather than map.
I think breaking changes should be avoided, but thats just my opinion. Adding a new variable makes sense in that regard.