ECS: known Terraform limitations
Some things people ask the terraform-aws-modules/ecs/aws module for
cannot be implemented by any module, in any registry. They are limits of
Terraform itself. This page collects the recurring ones for this module, says
plainly what causes each, gives the native workaround in full, and - where one
exists - shows the Operational Rule that removes the need for a fork.
Read the native option first
Every problem below names the workaround you can apply today without compliance.tf. Several of them are the right answer on their own. The rule is an alternative to maintaining a fork, not a replacement for a control AWS already offers you.
Terraform reverts the task definition my pipeline just deployed
The deployment pipeline registers a new task definition revision and points the service at it. Terraform holds the revision it created and rolls the service back to it on the next apply, which un-deploys whatever was released.
Why Terraform cannot fix this
Terraform's model is that it owns every attribute it can see. When a controller, an autoscaler, a scanner, or a deployment pipeline writes to one of those attributes, Terraform reads the new value as drift and plans to put its own value back. Both systems are behaving correctly; they simply disagree about who owns the field.
The only mechanism Terraform offers for splitting ownership is ignore_changes, and that is a lifecycle argument, which brings the literal-value restriction above with it. A module cannot accept the list of attributes to ignore as an input, so a module consumer has no way to declare the split.
| Repository | Issue | Title |
|---|---|---|
| hashicorp/terraform | #27360 | A method to override configuration and meta arguments within a module |
| hashicorp/terraform | #24188 | Support for dynamic blocks and meta-arguments |
| hashicorp/terraform-provider-aws | #19583 | Provider produced inconsistent final plan / an invalid new value for .tags_all |
The native workaround
The native fix is ignore_changes = [task_definition] on the service, which is a lifecycle argument. Fork this module and add the lifecycle block to the resource yourself. That works, and it is the honest answer - it is what module maintainers do when they need it. The cost is ongoing rather than one-off: the fork has to be re-synced with every upstream release, and each sync needs a review to confirm the block still lands on the resource it was meant for. A wrapper module does not avoid this, because the lifecycle block still has to sit inside the resource block, which is inside the module you did not write.
The alternative is to give ownership back to Terraform: have the pipeline update the Terraform configuration with the new image tag rather than calling the ECS API directly. That is more work in the pipeline and it removes the disagreement completely.
Managed fix
Native option only, today. compliance.tf has no rule for this yet, so the workaround above is the whole answer. The rule that would answer this is not built yet.
ECS service desired_count keeps resetting
Application Auto Scaling changes desired_count on the service. Terraform reads it as drift and scales the service back to the configured number.
Why Terraform cannot fix this
Terraform's model is that it owns every attribute it can see. When a controller, an autoscaler, a scanner, or a deployment pipeline writes to one of those attributes, Terraform reads the new value as drift and plans to put its own value back. Both systems are behaving correctly; they simply disagree about who owns the field.
The only mechanism Terraform offers for splitting ownership is ignore_changes, and that is a lifecycle argument, which brings the literal-value restriction above with it. A module cannot accept the list of attributes to ignore as an input, so a module consumer has no way to declare the split.
| Repository | Issue | Title |
|---|---|---|
| hashicorp/terraform | #27360 | A method to override configuration and meta arguments within a module |
| hashicorp/terraform | #24188 | Support for dynamic blocks and meta-arguments |
| hashicorp/terraform-provider-aws | #19583 | Provider produced inconsistent final plan / an invalid new value for .tags_all |
The native workaround
The native fix is ignore_changes = [desired_count] on the service, which is a lifecycle argument. Fork this module and add the lifecycle block to the resource yourself. That works, and it is the honest answer - it is what module maintainers do when they need it. The cost is ongoing rather than one-off: the fork has to be re-synced with every upstream release, and each sync needs a review to confirm the block still lands on the resource it was meant for. A wrapper module does not avoid this, because the lifecycle block still has to sit inside the resource block, which is inside the module you did not write.
Where the service is always autoscaled, leaving desired_count out of the configuration avoids the disagreement instead of suppressing it.
Managed fix
Native option only, today. compliance.tf has no rule for this yet, so the workaround above is the whole answer. The rule that would answer this is not built yet.
Using the maintained alternative
compliance.tf serves this module from a registry that applies the rules above
during terraform init. The change is the source line; the inputs and outputs are the upstream module's.
module "ecs" {
source = "https://cis.compliance.tf/terraform-aws-modules/ecs/aws"
# ... the same inputs you pass today ...
}None of the limitations above has a rule yet, so this module is served with
compliance controls only. The ?add_rules= parameter takes the ids of rules that
do exist - see Operational Rule Definitions.
Related
- Operational Rules - what rules are, and how they differ from compliance controls
- Operational Rule Definitions - the HCL of every selectable rule
- Stop paying the Terraform fork tax - why forking a module to add a
lifecycleblock costs more than it looks - ECS module reference - inputs, outputs, and the controls applied to it