terraform-aws-ecs icon indicating copy to clipboard operation
terraform-aws-ecs copied to clipboard

ordered_placement_strategy uses map instead of list, breaking strategy order

Open jworks-mwb opened this issue 1 month ago β€’ 5 comments

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:

  1. The order of placement strategies cannot be guaranteed
  2. This contradicts the variable's own description which states "List from top to bottom in order of precedence"
  3. 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
}

jworks-mwb avatar Nov 13 '25 19:11 jworks-mwb

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

lukzhang avatar Nov 14 '25 20:11 lukzhang

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 avatar Nov 14 '25 22:11 jworks-mwb

@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"
  }
]

lukzhang avatar Nov 15 '25 04:11 lukzhang

To avoid breaking the change, not sure if it would be better to add a new variable to use list rather than map.

lukzhang avatar Nov 18 '25 03:11 lukzhang

I think breaking changes should be avoided, but thats just my opinion. Adding a new variable makes sense in that regard.

jworks-mwb avatar Dec 04 '25 17:12 jworks-mwb