From 0f416d373b7c775d59492eb1b413d125bcb1a934 Mon Sep 17 00:00:00 2001 From: Gareth Denny <37297485+gpdenny@users.noreply.github.com> Date: Tue, 2 May 2023 13:21:19 +0100 Subject: [PATCH 01/11] feat: add ability to ignore load_balancer changes in ecs service --- modules/service/main.tf | 192 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 190 insertions(+), 2 deletions(-) diff --git a/modules/service/main.tf b/modules/service/main.tf index 35653411..6201f589 100644 --- a/modules/service/main.tf +++ b/modules/service/main.tf @@ -28,7 +28,7 @@ locals { } resource "aws_ecs_service" "this" { - count = var.create && !var.ignore_task_definition_changes ? 1 : 0 + count = var.create && !var.ignore_task_definition_changes && !var.ignore_load_balancer_changes ? 1 : 0 dynamic "alarms" { for_each = length(var.alarms) > 0 ? [var.alarms] : [] @@ -214,7 +214,7 @@ resource "aws_ecs_service" "this" { ################################################################################ resource "aws_ecs_service" "ignore_task_definition" { - count = var.create && var.ignore_task_definition_changes ? 1 : 0 + count = var.create && var.ignore_task_definition_changes && !var.ignore_load_balancer_changes ? 1 : 0 dynamic "alarms" { for_each = length(var.alarms) > 0 ? [var.alarms] : [] @@ -396,6 +396,194 @@ resource "aws_ecs_service" "ignore_task_definition" { } } +################################################################################ +# Service - Ignore `task_definition` and `load_balancer` +################################################################################ + +resource "aws_ecs_service" "ignore_task_definition_load_balancer" { + count = var.create && var.ignore_task_definition_changes && var.ignore_load_balancer_changes ? 1 : 0 + + dynamic "alarms" { + for_each = length(var.alarms) > 0 ? [var.alarms] : [] + + content { + alarm_names = alarms.value.alarm_names + enable = try(alarms.value.enable, true) + rollback = try(alarms.value.rollback, true) + } + } + + dynamic "capacity_provider_strategy" { + # Set by task set if deployment controller is external + for_each = { for k, v in var.capacity_provider_strategy : k => v if !local.is_external_deployment } + + content { + base = try(capacity_provider_strategy.value.base, null) + capacity_provider = capacity_provider_strategy.value.capacity_provider + weight = try(capacity_provider_strategy.value.weight, null) + } + } + + cluster = var.cluster_arn + + dynamic "deployment_circuit_breaker" { + for_each = length(var.deployment_circuit_breaker) > 0 ? [var.deployment_circuit_breaker] : [] + + content { + enable = deployment_circuit_breaker.value.enable + rollback = deployment_circuit_breaker.value.rollback + } + } + + dynamic "deployment_controller" { + for_each = length(var.deployment_controller) > 0 ? [var.deployment_controller] : [] + + content { + type = try(deployment_controller.value.type, null) + } + } + + deployment_maximum_percent = local.is_daemon || local.is_external_deployment ? null : var.deployment_maximum_percent + deployment_minimum_healthy_percent = local.is_daemon || local.is_external_deployment ? null : var.deployment_minimum_healthy_percent + desired_count = local.is_daemon || local.is_external_deployment ? null : var.desired_count + enable_ecs_managed_tags = var.enable_ecs_managed_tags + enable_execute_command = var.enable_execute_command + force_new_deployment = local.is_external_deployment ? null : var.force_new_deployment + health_check_grace_period_seconds = var.health_check_grace_period_seconds + iam_role = local.iam_role_arn + launch_type = local.is_external_deployment || length(var.capacity_provider_strategy) > 0 ? null : var.launch_type + + dynamic "load_balancer" { + # Set by task set if deployment controller is external + for_each = { for k, v in var.load_balancer : k => v if !local.is_external_deployment } + + content { + container_name = load_balancer.value.container_name + container_port = load_balancer.value.container_port + elb_name = try(load_balancer.value.elb_name, null) + target_group_arn = try(load_balancer.value.target_group_arn, null) + } + } + + name = var.name + + dynamic "network_configuration" { + # Set by task set if deployment controller is external + for_each = var.network_mode == "awsvpc" ? [{ for k, v in local.network_configuration : k => v if !local.is_external_deployment }] : [] + + content { + assign_public_ip = network_configuration.value.assign_public_ip + security_groups = network_configuration.value.security_groups + subnets = network_configuration.value.subnets + } + } + + dynamic "ordered_placement_strategy" { + for_each = var.ordered_placement_strategy + + content { + field = try(ordered_placement_strategy.value.field, null) + type = ordered_placement_strategy.value.type + } + } + + dynamic "placement_constraints" { + for_each = var.placement_constraints + + content { + expression = try(placement_constraints.value.expression, null) + type = placement_constraints.value.type + } + } + + # Set by task set if deployment controller is external + platform_version = local.is_fargate && !local.is_external_deployment ? var.platform_version : null + scheduling_strategy = local.is_fargate ? "REPLICA" : var.scheduling_strategy + + dynamic "service_connect_configuration" { + for_each = length(var.service_connect_configuration) > 0 ? [var.service_connect_configuration] : [] + + content { + enabled = try(service_connect_configuration.value.enabled, true) + + dynamic "log_configuration" { + for_each = try([service_connect_configuration.value.log_configuration], []) + + content { + log_driver = try(log_configuration.value.log_driver, null) + options = try(log_configuration.value.options, null) + + dynamic "secret_option" { + for_each = try(log_configuration.value.secret_option, []) + + content { + name = secret_option.value.name + value_from = secret_option.value.value_from + } + } + } + } + + namespace = lookup(service_connect_configuration.value, "namespace", null) + + dynamic "service" { + for_each = try([service_connect_configuration.value.service], []) + + content { + + dynamic "client_alias" { + for_each = try([service.value.client_alias], []) + + content { + dns_name = try(client_alias.value.dns_name, null) + port = client_alias.value.port + } + } + + discovery_name = try(service.value.discovery_name, null) + ingress_port_override = try(service.value.ingress_port_override, null) + port_name = service.value.port_name + } + } + } + } + + dynamic "service_registries" { + # Set by task set if deployment controller is external + for_each = length(var.service_registries) > 0 ? [{ for k, v in var.service_registries : k => v if !local.is_external_deployment }] : [] + + content { + container_name = try(service_registries.value.container_name, null) + container_port = try(service_registries.value.container_port, null) + port = try(service_registries.value.port, null) + registry_arn = service_registries.value.registry_arn + } + } + + task_definition = local.task_definition + triggers = var.triggers + wait_for_steady_state = var.wait_for_steady_state + + propagate_tags = var.propagate_tags + tags = var.tags + + timeouts { + create = try(var.timeouts.create, null) + update = try(var.timeouts.update, null) + delete = try(var.timeouts.delete, null) + } + + depends_on = [aws_iam_role_policy_attachment.service] + + lifecycle { + ignore_changes = [ + desired_count, # Always ignored + task_definition, + load_balancer + ] + } +} + ################################################################################ # Service - IAM Role ################################################################################ From 39c8f4ae62e91cb886c40a1a5ee8ecaa3a5b5ac5 Mon Sep 17 00:00:00 2001 From: Gareth Denny <37297485+gpdenny@users.noreply.github.com> Date: Tue, 2 May 2023 13:24:11 +0100 Subject: [PATCH 02/11] chore: add loadbalancer ignore variable --- modules/service/variables.tf | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/modules/service/variables.tf b/modules/service/variables.tf index 4c2136e7..a74da402 100644 --- a/modules/service/variables.tf +++ b/modules/service/variables.tf @@ -20,6 +20,12 @@ variable "ignore_task_definition_changes" { default = false } +variable "ignore_load_balancer_changes" { + description = "Whether changes to service `load_balancer` changes should be ignored" + type = bool + default = false +} + variable "alarms" { description = "Information about the CloudWatch alarms" type = any From 2c7a2416868b734f0d38156cf654e6f4f283ab84 Mon Sep 17 00:00:00 2001 From: Gareth Denny <37297485+gpdenny@users.noreply.github.com> Date: Tue, 2 May 2023 13:29:45 +0100 Subject: [PATCH 03/11] chore: update service output to include ignore_task_definition_load_balancer name --- modules/service/outputs.tf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/service/outputs.tf b/modules/service/outputs.tf index 059a64d7..06255e95 100644 --- a/modules/service/outputs.tf +++ b/modules/service/outputs.tf @@ -9,7 +9,7 @@ output "id" { output "name" { description = "Name of the service" - value = try(aws_ecs_service.this[0].name, aws_ecs_service.ignore_task_definition[0].name, null) + value = try(aws_ecs_service.this[0].name, aws_ecs_service.ignore_task_definition[0].name, aws_ecs_service.ignore_task_definition_load_balancer[0].name, null) } ################################################################################ From 465725232c7cf1d627c131a9cd235a51ed7fd924 Mon Sep 17 00:00:00 2001 From: Gareth Denny <37297485+gpdenny@users.noreply.github.com> Date: Tue, 2 May 2023 13:47:06 +0100 Subject: [PATCH 04/11] chore: ignore only target_group_arn --- modules/service/main.tf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/service/main.tf b/modules/service/main.tf index 6201f589..af0678f3 100644 --- a/modules/service/main.tf +++ b/modules/service/main.tf @@ -579,7 +579,7 @@ resource "aws_ecs_service" "ignore_task_definition_load_balancer" { ignore_changes = [ desired_count, # Always ignored task_definition, - load_balancer + load_balancer.target_group_arn ] } } From 883aaf3cdd95a8e18e28c17e55f0d09b7713e783 Mon Sep 17 00:00:00 2001 From: Gareth Denny <37297485+gpdenny@users.noreply.github.com> Date: Tue, 2 May 2023 13:50:00 +0100 Subject: [PATCH 05/11] chore: test ignore only target_group_arn --- modules/service/main.tf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/service/main.tf b/modules/service/main.tf index af0678f3..f25aae03 100644 --- a/modules/service/main.tf +++ b/modules/service/main.tf @@ -579,7 +579,7 @@ resource "aws_ecs_service" "ignore_task_definition_load_balancer" { ignore_changes = [ desired_count, # Always ignored task_definition, - load_balancer.target_group_arn + load_balancer["target_group_arn"] ] } } From 8f818c2408b7c4d41c507db5bfd9b5986a35fa31 Mon Sep 17 00:00:00 2001 From: Gareth Denny <37297485+gpdenny@users.noreply.github.com> Date: Tue, 2 May 2023 13:51:52 +0100 Subject: [PATCH 06/11] chore: remove test ignore only target_group_arn --- modules/service/main.tf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/service/main.tf b/modules/service/main.tf index f25aae03..6201f589 100644 --- a/modules/service/main.tf +++ b/modules/service/main.tf @@ -579,7 +579,7 @@ resource "aws_ecs_service" "ignore_task_definition_load_balancer" { ignore_changes = [ desired_count, # Always ignored task_definition, - load_balancer["target_group_arn"] + load_balancer ] } } From 0062063b55e139be44e7b0225c68c3ab2448c2e2 Mon Sep 17 00:00:00 2001 From: Gareth Denny <37297485+gpdenny@users.noreply.github.com> Date: Tue, 2 May 2023 14:00:10 +0100 Subject: [PATCH 07/11] chore: update README --- docs/README.md | 59 +++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 58 insertions(+), 1 deletion(-) diff --git a/docs/README.md b/docs/README.md index 2d938475..8701d258 100644 --- a/docs/README.md +++ b/docs/README.md @@ -74,9 +74,10 @@ The service sub-module creates one service that can be deployed onto a cluster. - Create an Amazon ECS service that ignores `desired_count`. This is intended for use when deploying task definition and container definition changes via Terraform - Create an Amazon ECS service that ignores `desired_count` and `task_definition`. This is intended to support a continuous deployment process that is responsible for updating the `image` and therefore the `task_definition` and `container_definition` while avoiding conflicts with Terraform. +- Create an Amazon ECS service that ignores `desired_count`, `task_definition` and `load_balancer`. This is intended to support a continuous deployment process using [Blue/Green deployment with CodeDeploy](https://docs.aws.amazon.com/AmazonECS/latest/userguide/deployment-type-bluegreen.html) which will allow updating the `target_group_arn` while avoiding conflicts with Terraform. - Amazon ECS task resources with the various configurations detailed below under [ECS Task](https://github.com/terraform-aws-modules/terraform-aws-ecs/blob/master/docs/README.md#ecs-task) -Since Terraform does not support variables within `lifecycle {}` blocks, its not possible to allow users to dynamically select which arguments they wish to ignore within the resources defined in the modules. Therefore, any arguments that should be ignored are statically set within the module definition. To somewhat mimic the behavior of allowing users to opt in/out of ignoring certain arguments, the module supports two different service definitions; one that ignores the `desired_count`, and one that ignores the `desired_count` and `task_definition`. The motivation and reasoning for these ignored argument configurations is detailed below: +Since Terraform does not support variables within `lifecycle {}` blocks, its not possible to allow users to dynamically select which arguments they wish to ignore within the resources defined in the modules. Therefore, any arguments that should be ignored are statically set within the module definition. To somewhat mimic the behavior of allowing users to opt in/out of ignoring certain arguments, the module supports three different service definitions; one that ignores the `desired_count`, and one that ignores the `desired_count` and `task_definition` and one that ignores `desired_count`, `task_definition` and `load_balancer`. The motivation and reasoning for these ignored argument configurations is detailed below: - `desired_count` is always ignored by the service module. It is very common to have autoscaling enabled for Amazon ECS services, allowing the number of tasks to scale based on the workload requirements. The scaling is managed via the `desired_count` that is managed by application auto scaling. This would directly conflict with Terraform if it was allowed to manage the `desired_count` as well. In addition, users have the ability to disable auto scaling if it does not suit their workload. In this case, the `desired_count` would be initially set by Terraform, and any further changes would need to be managed separately (outside of the service module). Users can make changes to the desired count of the service through the AWS console, AWS CLI, or AWS SDKs. One example workaround using Terraform is provided below, similar to the [EKS equivalent](https://github.com/bryantbiggs/eks-desired-size-hack): @@ -143,6 +144,62 @@ This could be expanded further to include the entire container definitions argum ECS Service

+- When using the above `ignore_task_definition_changes` setting, users can also elect to ignore changes to load balancers by setting `ignore_load_balancer_changes` to `true`. (Note: because of the aforementioned manner in which this psuedo-dynamic ignore change is being managed, changing this value after service creation will cause the entire service to be re-created. Change with caution!) This is intended to support the use of [Blue/Green deployment with CodeDeploy](https://docs.aws.amazon.com/AmazonECS/latest/userguide/deployment-type-bluegreen.html) which changes the the service's load balancer configuration. + +```hcl + + module "ecs_service" { + source = "terraform-aws-modules/ecs/aws//modules/service" + + # ... omitted for brevity + + ignore_task_definition_changes = true + ignore_load_balancer_changes = true + } + + resource "aws_lb_target_group" "this" { + for_each = { blue = {}, green = {} } + name = each.key + + # ... omitted for brevity + } + + resource "aws_codedeploy_app" "this" { + name = "my-app" + compute_platform = "ECS" + } + + resource "aws_codedeploy_deployment_group" "this" { + deployment_group_name = "my-deployment-group" + app_name = aws_codedeploy_app.this.name + + deployment_config_name = "CodeDeployDefault.ECSAllAtOnce" + + deployment_style { + deployment_option = "WITH_TRAFFIC_CONTROL" + deployment_type = "BLUE_GREEN" + } + + # ... omitted for brevity + + load_balancer_info { + target_group_pair_info { + prod_traffic_route { + listener_arns = ["my-listener-arn"] + } + + target_group { + name = aws_lb_target_group.this["blue"].name + } + + target_group { + name = aws_lb_target_group.this["green"].name + } + } + } + } +``` + ### Task ECS tasks are the byproduct of task definitions and task sets. In addition to what has been described above, the service module supports the following task level configurations: From 2e5c59af616cef01cc514d02729dc811af2c5684 Mon Sep 17 00:00:00 2001 From: Gareth Denny <37297485+gpdenny@users.noreply.github.com> Date: Tue, 2 May 2023 14:54:51 +0100 Subject: [PATCH 08/11] chore: precommit run --- modules/service/README.md | 2 ++ wrappers/service/main.tf | 1 + 2 files changed, 3 insertions(+) diff --git a/modules/service/README.md b/modules/service/README.md index af1ac146..7e740ee7 100644 --- a/modules/service/README.md +++ b/modules/service/README.md @@ -186,6 +186,7 @@ module "ecs_service" { | [aws_appautoscaling_scheduled_action.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/appautoscaling_scheduled_action) | resource | | [aws_appautoscaling_target.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/appautoscaling_target) | resource | | [aws_ecs_service.ignore_task_definition](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/ecs_service) | resource | +| [aws_ecs_service.ignore_task_definition_load_balancer](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/ecs_service) | resource | | [aws_ecs_service.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/ecs_service) | resource | | [aws_ecs_task_definition.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/ecs_task_definition) | resource | | [aws_ecs_task_set.ignore_task_definition](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/ecs_task_set) | resource | @@ -258,6 +259,7 @@ module "ecs_service" { | [iam\_role\_statements](#input\_iam\_role\_statements) | A map of IAM policy [statements](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document#statement) for custom permission usage | `any` | `{}` | no | | [iam\_role\_tags](#input\_iam\_role\_tags) | A map of additional tags to add to the IAM role created | `map(string)` | `{}` | no | | [iam\_role\_use\_name\_prefix](#input\_iam\_role\_use\_name\_prefix) | Determines whether the IAM role name (`iam_role_name`) is used as a prefix | `bool` | `true` | no | +| [ignore\_load\_balancer\_changes](#input\_ignore\_load\_balancer\_changes) | Whether changes to service `load_balancer` changes should be ignored | `bool` | `false` | no | | [ignore\_task\_definition\_changes](#input\_ignore\_task\_definition\_changes) | Whether changes to service `task_definition` changes should be ignored | `bool` | `false` | no | | [inference\_accelerator](#input\_inference\_accelerator) | Configuration block(s) with Inference Accelerators settings | `any` | `{}` | no | | [ipc\_mode](#input\_ipc\_mode) | IPC resource namespace to be used for the containers in the task The valid values are `host`, `task`, and `none` | `string` | `null` | no | diff --git a/wrappers/service/main.tf b/wrappers/service/main.tf index 42ba6f14..b5d28d50 100644 --- a/wrappers/service/main.tf +++ b/wrappers/service/main.tf @@ -6,6 +6,7 @@ module "wrapper" { create = try(each.value.create, var.defaults.create, true) tags = try(each.value.tags, var.defaults.tags, {}) ignore_task_definition_changes = try(each.value.ignore_task_definition_changes, var.defaults.ignore_task_definition_changes, false) + ignore_load_balancer_changes = try(each.value.ignore_load_balancer_changes, var.defaults.ignore_load_balancer_changes, false) alarms = try(each.value.alarms, var.defaults.alarms, {}) capacity_provider_strategy = try(each.value.capacity_provider_strategy, var.defaults.capacity_provider_strategy, {}) cluster_arn = try(each.value.cluster_arn, var.defaults.cluster_arn, "") From 6f16facd988c7e77006a2b047533f0487315eb82 Mon Sep 17 00:00:00 2001 From: Bryant Biggs Date: Sat, 3 Jun 2023 07:56:15 -0400 Subject: [PATCH 09/11] fix: Update to collapse changes into existing service --- .pre-commit-config.yaml | 2 +- docs/README.md | 10 +- modules/service/README.md | 2 - modules/service/main.tf | 193 +----------------------- modules/service/outputs.tf | 2 +- modules/service/variables.tf | 6 - wrappers/README.md | 6 +- wrappers/cluster/README.md | 6 +- wrappers/container-definition/README.md | 6 +- wrappers/service/README.md | 6 +- wrappers/service/main.tf | 1 - 11 files changed, 23 insertions(+), 217 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index e940bf7d..eedadaab 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,6 +1,6 @@ repos: - repo: https://github.com/antonbabenko/pre-commit-terraform - rev: v1.77.3 + rev: v1.80.0 hooks: - id: terraform_fmt - id: terraform_wrapper_module_for_each diff --git a/docs/README.md b/docs/README.md index 8701d258..7385236a 100644 --- a/docs/README.md +++ b/docs/README.md @@ -144,21 +144,23 @@ This could be expanded further to include the entire container definitions argum ECS Service

-- When using the above `ignore_task_definition_changes` setting, users can also elect to ignore changes to load balancers by setting `ignore_load_balancer_changes` to `true`. (Note: because of the aforementioned manner in which this psuedo-dynamic ignore change is being managed, changing this value after service creation will cause the entire service to be re-created. Change with caution!) This is intended to support the use of [Blue/Green deployment with CodeDeploy](https://docs.aws.amazon.com/AmazonECS/latest/userguide/deployment-type-bluegreen.html) which changes the the service's load balancer configuration. +- When using the above `ignore_task_definition_changes` setting, changes to the `load_balancer` argument are also ignored. This is intended to support the use of [Blue/Green deployment with CodeDeploy](https://docs.aws.amazon.com/AmazonECS/latest/userguide/deployment-type-bluegreen.html) which changes the the service's load balancer configuration. (Note: the ignored changes to the `load_balancer` were added after the fact which is why the variable name does not reflect this behavior. In a future major release, this variable will be updated to better reflect its behavior) ```hcl - module "ecs_service" { source = "terraform-aws-modules/ecs/aws//modules/service" # ... omitted for brevity ignore_task_definition_changes = true - ignore_load_balancer_changes = true } resource "aws_lb_target_group" "this" { - for_each = { blue = {}, green = {} } + for_each = { + blue = {}, + green = {} + } + name = each.key # ... omitted for brevity diff --git a/modules/service/README.md b/modules/service/README.md index 3373558d..1bb26b64 100644 --- a/modules/service/README.md +++ b/modules/service/README.md @@ -186,7 +186,6 @@ module "ecs_service" { | [aws_appautoscaling_scheduled_action.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/appautoscaling_scheduled_action) | resource | | [aws_appautoscaling_target.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/appautoscaling_target) | resource | | [aws_ecs_service.ignore_task_definition](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/ecs_service) | resource | -| [aws_ecs_service.ignore_task_definition_load_balancer](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/ecs_service) | resource | | [aws_ecs_service.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/ecs_service) | resource | | [aws_ecs_task_definition.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/ecs_task_definition) | resource | | [aws_ecs_task_set.ignore_task_definition](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/ecs_task_set) | resource | @@ -259,7 +258,6 @@ module "ecs_service" { | [iam\_role\_statements](#input\_iam\_role\_statements) | A map of IAM policy [statements](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document#statement) for custom permission usage | `any` | `{}` | no | | [iam\_role\_tags](#input\_iam\_role\_tags) | A map of additional tags to add to the IAM role created | `map(string)` | `{}` | no | | [iam\_role\_use\_name\_prefix](#input\_iam\_role\_use\_name\_prefix) | Determines whether the IAM role name (`iam_role_name`) is used as a prefix | `bool` | `true` | no | -| [ignore\_load\_balancer\_changes](#input\_ignore\_load\_balancer\_changes) | Whether changes to service `load_balancer` changes should be ignored | `bool` | `false` | no | | [ignore\_task\_definition\_changes](#input\_ignore\_task\_definition\_changes) | Whether changes to service `task_definition` changes should be ignored | `bool` | `false` | no | | [inference\_accelerator](#input\_inference\_accelerator) | Configuration block(s) with Inference Accelerators settings | `any` | `{}` | no | | [ipc\_mode](#input\_ipc\_mode) | IPC resource namespace to be used for the containers in the task The valid values are `host`, `task`, and `none` | `string` | `null` | no | diff --git a/modules/service/main.tf b/modules/service/main.tf index 6201f589..944a2576 100644 --- a/modules/service/main.tf +++ b/modules/service/main.tf @@ -28,7 +28,7 @@ locals { } resource "aws_ecs_service" "this" { - count = var.create && !var.ignore_task_definition_changes && !var.ignore_load_balancer_changes ? 1 : 0 + count = var.create && !var.ignore_task_definition_changes ? 1 : 0 dynamic "alarms" { for_each = length(var.alarms) > 0 ? [var.alarms] : [] @@ -214,7 +214,7 @@ resource "aws_ecs_service" "this" { ################################################################################ resource "aws_ecs_service" "ignore_task_definition" { - count = var.create && var.ignore_task_definition_changes && !var.ignore_load_balancer_changes ? 1 : 0 + count = var.create && var.ignore_task_definition_changes ? 1 : 0 dynamic "alarms" { for_each = length(var.alarms) > 0 ? [var.alarms] : [] @@ -392,194 +392,7 @@ resource "aws_ecs_service" "ignore_task_definition" { ignore_changes = [ desired_count, # Always ignored task_definition, - ] - } -} - -################################################################################ -# Service - Ignore `task_definition` and `load_balancer` -################################################################################ - -resource "aws_ecs_service" "ignore_task_definition_load_balancer" { - count = var.create && var.ignore_task_definition_changes && var.ignore_load_balancer_changes ? 1 : 0 - - dynamic "alarms" { - for_each = length(var.alarms) > 0 ? [var.alarms] : [] - - content { - alarm_names = alarms.value.alarm_names - enable = try(alarms.value.enable, true) - rollback = try(alarms.value.rollback, true) - } - } - - dynamic "capacity_provider_strategy" { - # Set by task set if deployment controller is external - for_each = { for k, v in var.capacity_provider_strategy : k => v if !local.is_external_deployment } - - content { - base = try(capacity_provider_strategy.value.base, null) - capacity_provider = capacity_provider_strategy.value.capacity_provider - weight = try(capacity_provider_strategy.value.weight, null) - } - } - - cluster = var.cluster_arn - - dynamic "deployment_circuit_breaker" { - for_each = length(var.deployment_circuit_breaker) > 0 ? [var.deployment_circuit_breaker] : [] - - content { - enable = deployment_circuit_breaker.value.enable - rollback = deployment_circuit_breaker.value.rollback - } - } - - dynamic "deployment_controller" { - for_each = length(var.deployment_controller) > 0 ? [var.deployment_controller] : [] - - content { - type = try(deployment_controller.value.type, null) - } - } - - deployment_maximum_percent = local.is_daemon || local.is_external_deployment ? null : var.deployment_maximum_percent - deployment_minimum_healthy_percent = local.is_daemon || local.is_external_deployment ? null : var.deployment_minimum_healthy_percent - desired_count = local.is_daemon || local.is_external_deployment ? null : var.desired_count - enable_ecs_managed_tags = var.enable_ecs_managed_tags - enable_execute_command = var.enable_execute_command - force_new_deployment = local.is_external_deployment ? null : var.force_new_deployment - health_check_grace_period_seconds = var.health_check_grace_period_seconds - iam_role = local.iam_role_arn - launch_type = local.is_external_deployment || length(var.capacity_provider_strategy) > 0 ? null : var.launch_type - - dynamic "load_balancer" { - # Set by task set if deployment controller is external - for_each = { for k, v in var.load_balancer : k => v if !local.is_external_deployment } - - content { - container_name = load_balancer.value.container_name - container_port = load_balancer.value.container_port - elb_name = try(load_balancer.value.elb_name, null) - target_group_arn = try(load_balancer.value.target_group_arn, null) - } - } - - name = var.name - - dynamic "network_configuration" { - # Set by task set if deployment controller is external - for_each = var.network_mode == "awsvpc" ? [{ for k, v in local.network_configuration : k => v if !local.is_external_deployment }] : [] - - content { - assign_public_ip = network_configuration.value.assign_public_ip - security_groups = network_configuration.value.security_groups - subnets = network_configuration.value.subnets - } - } - - dynamic "ordered_placement_strategy" { - for_each = var.ordered_placement_strategy - - content { - field = try(ordered_placement_strategy.value.field, null) - type = ordered_placement_strategy.value.type - } - } - - dynamic "placement_constraints" { - for_each = var.placement_constraints - - content { - expression = try(placement_constraints.value.expression, null) - type = placement_constraints.value.type - } - } - - # Set by task set if deployment controller is external - platform_version = local.is_fargate && !local.is_external_deployment ? var.platform_version : null - scheduling_strategy = local.is_fargate ? "REPLICA" : var.scheduling_strategy - - dynamic "service_connect_configuration" { - for_each = length(var.service_connect_configuration) > 0 ? [var.service_connect_configuration] : [] - - content { - enabled = try(service_connect_configuration.value.enabled, true) - - dynamic "log_configuration" { - for_each = try([service_connect_configuration.value.log_configuration], []) - - content { - log_driver = try(log_configuration.value.log_driver, null) - options = try(log_configuration.value.options, null) - - dynamic "secret_option" { - for_each = try(log_configuration.value.secret_option, []) - - content { - name = secret_option.value.name - value_from = secret_option.value.value_from - } - } - } - } - - namespace = lookup(service_connect_configuration.value, "namespace", null) - - dynamic "service" { - for_each = try([service_connect_configuration.value.service], []) - - content { - - dynamic "client_alias" { - for_each = try([service.value.client_alias], []) - - content { - dns_name = try(client_alias.value.dns_name, null) - port = client_alias.value.port - } - } - - discovery_name = try(service.value.discovery_name, null) - ingress_port_override = try(service.value.ingress_port_override, null) - port_name = service.value.port_name - } - } - } - } - - dynamic "service_registries" { - # Set by task set if deployment controller is external - for_each = length(var.service_registries) > 0 ? [{ for k, v in var.service_registries : k => v if !local.is_external_deployment }] : [] - - content { - container_name = try(service_registries.value.container_name, null) - container_port = try(service_registries.value.container_port, null) - port = try(service_registries.value.port, null) - registry_arn = service_registries.value.registry_arn - } - } - - task_definition = local.task_definition - triggers = var.triggers - wait_for_steady_state = var.wait_for_steady_state - - propagate_tags = var.propagate_tags - tags = var.tags - - timeouts { - create = try(var.timeouts.create, null) - update = try(var.timeouts.update, null) - delete = try(var.timeouts.delete, null) - } - - depends_on = [aws_iam_role_policy_attachment.service] - - lifecycle { - ignore_changes = [ - desired_count, # Always ignored - task_definition, - load_balancer + load_balancer, ] } } diff --git a/modules/service/outputs.tf b/modules/service/outputs.tf index 06255e95..059a64d7 100644 --- a/modules/service/outputs.tf +++ b/modules/service/outputs.tf @@ -9,7 +9,7 @@ output "id" { output "name" { description = "Name of the service" - value = try(aws_ecs_service.this[0].name, aws_ecs_service.ignore_task_definition[0].name, aws_ecs_service.ignore_task_definition_load_balancer[0].name, null) + value = try(aws_ecs_service.this[0].name, aws_ecs_service.ignore_task_definition[0].name, null) } ################################################################################ diff --git a/modules/service/variables.tf b/modules/service/variables.tf index a74da402..4c2136e7 100644 --- a/modules/service/variables.tf +++ b/modules/service/variables.tf @@ -20,12 +20,6 @@ variable "ignore_task_definition_changes" { default = false } -variable "ignore_load_balancer_changes" { - description = "Whether changes to service `load_balancer` changes should be ignored" - type = bool - default = false -} - variable "alarms" { description = "Information about the CloudWatch alarms" type = any diff --git a/wrappers/README.md b/wrappers/README.md index 449acd8a..7e76c915 100644 --- a/wrappers/README.md +++ b/wrappers/README.md @@ -12,9 +12,9 @@ This wrapper does not implement any extra functionality. ```hcl terraform { - source = "tfr:///terraform-aws-modules/ecs/aws//wrappers" + source = "tfr:///terraform-aws-modules/pr/aws//wrappers" # Alternative source: - # source = "git::git@github.com:terraform-aws-modules/terraform-aws-ecs.git//wrappers?ref=master" + # source = "git::git@github.com:terraform-aws-modules/terraform-aws-pr.git//wrappers?ref=master" } inputs = { @@ -42,7 +42,7 @@ inputs = { ```hcl module "wrapper" { - source = "terraform-aws-modules/ecs/aws//wrappers" + source = "terraform-aws-modules/pr/aws//wrappers" defaults = { # Default values create = true diff --git a/wrappers/cluster/README.md b/wrappers/cluster/README.md index 1a795281..1fc8a06e 100644 --- a/wrappers/cluster/README.md +++ b/wrappers/cluster/README.md @@ -12,9 +12,9 @@ This wrapper does not implement any extra functionality. ```hcl terraform { - source = "tfr:///terraform-aws-modules/ecs/aws//wrappers/cluster" + source = "tfr:///terraform-aws-modules/pr/aws//wrappers/cluster" # Alternative source: - # source = "git::git@github.com:terraform-aws-modules/terraform-aws-ecs.git//wrappers/cluster?ref=master" + # source = "git::git@github.com:terraform-aws-modules/terraform-aws-pr.git//wrappers/cluster?ref=master" } inputs = { @@ -42,7 +42,7 @@ inputs = { ```hcl module "wrapper" { - source = "terraform-aws-modules/ecs/aws//wrappers/cluster" + source = "terraform-aws-modules/pr/aws//wrappers/cluster" defaults = { # Default values create = true diff --git a/wrappers/container-definition/README.md b/wrappers/container-definition/README.md index 4731aa9a..c54844ef 100644 --- a/wrappers/container-definition/README.md +++ b/wrappers/container-definition/README.md @@ -12,9 +12,9 @@ This wrapper does not implement any extra functionality. ```hcl terraform { - source = "tfr:///terraform-aws-modules/ecs/aws//wrappers/container-definition" + source = "tfr:///terraform-aws-modules/pr/aws//wrappers/container-definition" # Alternative source: - # source = "git::git@github.com:terraform-aws-modules/terraform-aws-ecs.git//wrappers/container-definition?ref=master" + # source = "git::git@github.com:terraform-aws-modules/terraform-aws-pr.git//wrappers/container-definition?ref=master" } inputs = { @@ -42,7 +42,7 @@ inputs = { ```hcl module "wrapper" { - source = "terraform-aws-modules/ecs/aws//wrappers/container-definition" + source = "terraform-aws-modules/pr/aws//wrappers/container-definition" defaults = { # Default values create = true diff --git a/wrappers/service/README.md b/wrappers/service/README.md index 219da916..2fff6bfc 100644 --- a/wrappers/service/README.md +++ b/wrappers/service/README.md @@ -12,9 +12,9 @@ This wrapper does not implement any extra functionality. ```hcl terraform { - source = "tfr:///terraform-aws-modules/ecs/aws//wrappers/service" + source = "tfr:///terraform-aws-modules/pr/aws//wrappers/service" # Alternative source: - # source = "git::git@github.com:terraform-aws-modules/terraform-aws-ecs.git//wrappers/service?ref=master" + # source = "git::git@github.com:terraform-aws-modules/terraform-aws-pr.git//wrappers/service?ref=master" } inputs = { @@ -42,7 +42,7 @@ inputs = { ```hcl module "wrapper" { - source = "terraform-aws-modules/ecs/aws//wrappers/service" + source = "terraform-aws-modules/pr/aws//wrappers/service" defaults = { # Default values create = true diff --git a/wrappers/service/main.tf b/wrappers/service/main.tf index b5d28d50..42ba6f14 100644 --- a/wrappers/service/main.tf +++ b/wrappers/service/main.tf @@ -6,7 +6,6 @@ module "wrapper" { create = try(each.value.create, var.defaults.create, true) tags = try(each.value.tags, var.defaults.tags, {}) ignore_task_definition_changes = try(each.value.ignore_task_definition_changes, var.defaults.ignore_task_definition_changes, false) - ignore_load_balancer_changes = try(each.value.ignore_load_balancer_changes, var.defaults.ignore_load_balancer_changes, false) alarms = try(each.value.alarms, var.defaults.alarms, {}) capacity_provider_strategy = try(each.value.capacity_provider_strategy, var.defaults.capacity_provider_strategy, {}) cluster_arn = try(each.value.cluster_arn, var.defaults.cluster_arn, "") From 85c8f35f517449e4a585fa52e3c5fcd3db41eb32 Mon Sep 17 00:00:00 2001 From: Bryant Biggs Date: Sat, 3 Jun 2023 08:01:36 -0400 Subject: [PATCH 10/11] chore: Correct wrappers and docs --- docs/README.md | 5 ++--- wrappers/README.md | 6 +++--- wrappers/cluster/README.md | 6 +++--- wrappers/container-definition/README.md | 6 +++--- wrappers/service/README.md | 6 +++--- 5 files changed, 14 insertions(+), 15 deletions(-) diff --git a/docs/README.md b/docs/README.md index 7385236a..83f3bd04 100644 --- a/docs/README.md +++ b/docs/README.md @@ -73,11 +73,10 @@ This module supports creating a task execution IAM role in two different ways to The service sub-module creates one service that can be deployed onto a cluster. The service sub-module allows users to: - Create an Amazon ECS service that ignores `desired_count`. This is intended for use when deploying task definition and container definition changes via Terraform -- Create an Amazon ECS service that ignores `desired_count` and `task_definition`. This is intended to support a continuous deployment process that is responsible for updating the `image` and therefore the `task_definition` and `container_definition` while avoiding conflicts with Terraform. -- Create an Amazon ECS service that ignores `desired_count`, `task_definition` and `load_balancer`. This is intended to support a continuous deployment process using [Blue/Green deployment with CodeDeploy](https://docs.aws.amazon.com/AmazonECS/latest/userguide/deployment-type-bluegreen.html) which will allow updating the `target_group_arn` while avoiding conflicts with Terraform. +- Create an Amazon ECS service that ignores `desired_count` and `task_definition`, and `load_balancer`. This is intended to support a continuous deployment process that is responsible for updating the `image` and therefore the `task_definition` and `container_definition` while avoiding conflicts with Terraform. - Amazon ECS task resources with the various configurations detailed below under [ECS Task](https://github.com/terraform-aws-modules/terraform-aws-ecs/blob/master/docs/README.md#ecs-task) -Since Terraform does not support variables within `lifecycle {}` blocks, its not possible to allow users to dynamically select which arguments they wish to ignore within the resources defined in the modules. Therefore, any arguments that should be ignored are statically set within the module definition. To somewhat mimic the behavior of allowing users to opt in/out of ignoring certain arguments, the module supports three different service definitions; one that ignores the `desired_count`, and one that ignores the `desired_count` and `task_definition` and one that ignores `desired_count`, `task_definition` and `load_balancer`. The motivation and reasoning for these ignored argument configurations is detailed below: +Since Terraform does not support variables within `lifecycle {}` blocks, its not possible to allow users to dynamically select which arguments they wish to ignore within the resources defined in the modules. Therefore, any arguments that should be ignored are statically set within the module definition. To somewhat mimic the behavior of allowing users to opt in/out of ignoring certain arguments, the module supports two different service definitions; one that ignores the `desired_count`, and one that ignores `desired_count`, `task_definition` and `load_balancer`. The motivation and reasoning for these ignored argument configurations is detailed below: - `desired_count` is always ignored by the service module. It is very common to have autoscaling enabled for Amazon ECS services, allowing the number of tasks to scale based on the workload requirements. The scaling is managed via the `desired_count` that is managed by application auto scaling. This would directly conflict with Terraform if it was allowed to manage the `desired_count` as well. In addition, users have the ability to disable auto scaling if it does not suit their workload. In this case, the `desired_count` would be initially set by Terraform, and any further changes would need to be managed separately (outside of the service module). Users can make changes to the desired count of the service through the AWS console, AWS CLI, or AWS SDKs. One example workaround using Terraform is provided below, similar to the [EKS equivalent](https://github.com/bryantbiggs/eks-desired-size-hack): diff --git a/wrappers/README.md b/wrappers/README.md index 7e76c915..449acd8a 100644 --- a/wrappers/README.md +++ b/wrappers/README.md @@ -12,9 +12,9 @@ This wrapper does not implement any extra functionality. ```hcl terraform { - source = "tfr:///terraform-aws-modules/pr/aws//wrappers" + source = "tfr:///terraform-aws-modules/ecs/aws//wrappers" # Alternative source: - # source = "git::git@github.com:terraform-aws-modules/terraform-aws-pr.git//wrappers?ref=master" + # source = "git::git@github.com:terraform-aws-modules/terraform-aws-ecs.git//wrappers?ref=master" } inputs = { @@ -42,7 +42,7 @@ inputs = { ```hcl module "wrapper" { - source = "terraform-aws-modules/pr/aws//wrappers" + source = "terraform-aws-modules/ecs/aws//wrappers" defaults = { # Default values create = true diff --git a/wrappers/cluster/README.md b/wrappers/cluster/README.md index 1fc8a06e..1a795281 100644 --- a/wrappers/cluster/README.md +++ b/wrappers/cluster/README.md @@ -12,9 +12,9 @@ This wrapper does not implement any extra functionality. ```hcl terraform { - source = "tfr:///terraform-aws-modules/pr/aws//wrappers/cluster" + source = "tfr:///terraform-aws-modules/ecs/aws//wrappers/cluster" # Alternative source: - # source = "git::git@github.com:terraform-aws-modules/terraform-aws-pr.git//wrappers/cluster?ref=master" + # source = "git::git@github.com:terraform-aws-modules/terraform-aws-ecs.git//wrappers/cluster?ref=master" } inputs = { @@ -42,7 +42,7 @@ inputs = { ```hcl module "wrapper" { - source = "terraform-aws-modules/pr/aws//wrappers/cluster" + source = "terraform-aws-modules/ecs/aws//wrappers/cluster" defaults = { # Default values create = true diff --git a/wrappers/container-definition/README.md b/wrappers/container-definition/README.md index c54844ef..4731aa9a 100644 --- a/wrappers/container-definition/README.md +++ b/wrappers/container-definition/README.md @@ -12,9 +12,9 @@ This wrapper does not implement any extra functionality. ```hcl terraform { - source = "tfr:///terraform-aws-modules/pr/aws//wrappers/container-definition" + source = "tfr:///terraform-aws-modules/ecs/aws//wrappers/container-definition" # Alternative source: - # source = "git::git@github.com:terraform-aws-modules/terraform-aws-pr.git//wrappers/container-definition?ref=master" + # source = "git::git@github.com:terraform-aws-modules/terraform-aws-ecs.git//wrappers/container-definition?ref=master" } inputs = { @@ -42,7 +42,7 @@ inputs = { ```hcl module "wrapper" { - source = "terraform-aws-modules/pr/aws//wrappers/container-definition" + source = "terraform-aws-modules/ecs/aws//wrappers/container-definition" defaults = { # Default values create = true diff --git a/wrappers/service/README.md b/wrappers/service/README.md index 2fff6bfc..219da916 100644 --- a/wrappers/service/README.md +++ b/wrappers/service/README.md @@ -12,9 +12,9 @@ This wrapper does not implement any extra functionality. ```hcl terraform { - source = "tfr:///terraform-aws-modules/pr/aws//wrappers/service" + source = "tfr:///terraform-aws-modules/ecs/aws//wrappers/service" # Alternative source: - # source = "git::git@github.com:terraform-aws-modules/terraform-aws-pr.git//wrappers/service?ref=master" + # source = "git::git@github.com:terraform-aws-modules/terraform-aws-ecs.git//wrappers/service?ref=master" } inputs = { @@ -42,7 +42,7 @@ inputs = { ```hcl module "wrapper" { - source = "terraform-aws-modules/pr/aws//wrappers/service" + source = "terraform-aws-modules/ecs/aws//wrappers/service" defaults = { # Default values create = true From 47b703365ed5d0af65820a73b34dddf0f97e6039 Mon Sep 17 00:00:00 2001 From: Anton Babenko Date: Mon, 5 Jun 2023 10:26:53 -0700 Subject: [PATCH 11/11] Update docs/README.md --- docs/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/README.md b/docs/README.md index 83f3bd04..80f0b9f9 100644 --- a/docs/README.md +++ b/docs/README.md @@ -160,7 +160,7 @@ This could be expanded further to include the entire container definitions argum green = {} } - name = each.key + name = each.key # ... omitted for brevity }