From 4d16e1fffee2ab0229f7664bcf0b1516b7fc524f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20AUFORT?= Date: Mon, 15 Sep 2025 07:55:37 +0000 Subject: [PATCH] feat: Add new vpc_id input Sometimes, it happens that Terraform tries to recreate the security group of the ECS service whereas the VPC did not actually change. To avoid this issue, let's use the dependency inversion principle (described here https://developer.hashicorp.com/terraform/language/modules/develop/composition#dependency-inversion) by passing the VPC ID as an input. --- README.md | 2 ++ examples/complete/main.tf | 1 + modules/service/README.md | 1 + modules/service/main.tf | 2 +- modules/service/variables.tf | 7 +++++++ wrappers/service/main.tf | 1 + 6 files changed, 13 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 986ad05..dedfd9f 100644 --- a/README.md +++ b/README.md @@ -119,6 +119,8 @@ module "ecs" { } subnet_ids = ["subnet-abcde012", "subnet-bcde012a", "subnet-fghi345a"] + vpc_id = "vpc-jklmn789" + security_group_ingress_rules = { alb_3000 = { description = "Service port" diff --git a/examples/complete/main.tf b/examples/complete/main.tf index af37c00..9df8176 100644 --- a/examples/complete/main.tf +++ b/examples/complete/main.tf @@ -177,6 +177,7 @@ module "ecs" { ] subnet_ids = module.vpc.private_subnets + vpc_id = module.vpc.vpc_id availability_zone_rebalancing = "ENABLED" security_group_ingress_rules = { alb_3000 = { diff --git a/modules/service/README.md b/modules/service/README.md index 790b519..ef33a6f 100644 --- a/modules/service/README.md +++ b/modules/service/README.md @@ -342,6 +342,7 @@ module "ecs_service" { | [triggers](#input\_triggers) | Map of arbitrary keys and values that, when changed, will trigger an in-place update (redeployment). Useful with `timestamp()` | `map(string)` | `null` | no | | [volume](#input\_volume) | Configuration block for volumes that containers in your task may use |
map(object({
configure_at_launch = optional(bool)
docker_volume_configuration = optional(object({
autoprovision = optional(bool)
driver = optional(string)
driver_opts = optional(map(string))
labels = optional(map(string))
scope = optional(string)
}))
efs_volume_configuration = optional(object({
authorization_config = optional(object({
access_point_id = optional(string)
iam = optional(string)
}))
file_system_id = string
root_directory = optional(string)
transit_encryption = optional(string)
transit_encryption_port = optional(number)
}))
fsx_windows_file_server_volume_configuration = optional(object({
authorization_config = optional(object({
credentials_parameter = string
domain = string
}))
file_system_id = string
root_directory = string
}))
host_path = optional(string)
name = optional(string)
}))
| `null` | no | | [volume\_configuration](#input\_volume\_configuration) | Configuration for a volume specified in the task definition as a volume that is configured at launch time |
object({
name = string
managed_ebs_volume = object({
encrypted = optional(bool)
file_system_type = optional(string)
iops = optional(number)
kms_key_id = optional(string)
size_in_gb = optional(number)
snapshot_id = optional(string)
tag_specifications = optional(list(object({
propagate_tags = optional(string, "TASK_DEFINITION")
resource_type = string
tags = optional(map(string))
})))
throughput = optional(number)
volume_type = optional(string)
})
})
| `null` | no | +| [vpc\_id](#input\_vpc\_id) | The VPC ID where to deploy the task or service. If not provided, the VPC ID is retrieved from the subnets. | `string` | `null` | no | | [vpc\_lattice\_configurations](#input\_vpc\_lattice\_configurations) | The VPC Lattice configuration for your service that allows Lattice to connect, secure, and monitor your service across multiple accounts and VPCs |
object({
role_arn = string
target_group_arn = string
port_name = string
})
| `null` | no | | [wait\_for\_steady\_state](#input\_wait\_for\_steady\_state) | If true, Terraform will wait for the service to reach a steady state before continuing. Default is `false` | `bool` | `null` | no | | [wait\_until\_stable](#input\_wait\_until\_stable) | Whether terraform should wait until the task set has reached `STEADY_STATE` | `bool` | `null` | no | diff --git a/modules/service/main.tf b/modules/service/main.tf index e8d9100..cc7cae3 100644 --- a/modules/service/main.tf +++ b/modules/service/main.tf @@ -1651,7 +1651,7 @@ resource "aws_security_group" "this" { name = var.security_group_use_name_prefix ? null : local.security_group_name name_prefix = var.security_group_use_name_prefix ? "${local.security_group_name}-" : null description = var.security_group_description - vpc_id = data.aws_subnet.this[0].vpc_id + vpc_id = var.vpc_id != null ? var.vpc_id : data.aws_subnet.this[0].vpc_id tags = merge( var.tags, diff --git a/modules/service/variables.tf b/modules/service/variables.tf index 9694903..20f79e0 100644 --- a/modules/service/variables.tf +++ b/modules/service/variables.tf @@ -208,6 +208,13 @@ variable "subnet_ids" { nullable = false } +variable "vpc_id" { + description = "The VPC ID where to deploy the task or service. If not provided, the VPC ID is retrieved from the subnets." + type = string + default = null + nullable = true +} + 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({ diff --git a/wrappers/service/main.tf b/wrappers/service/main.tf index ab40fd7..cf6f0e1 100644 --- a/wrappers/service/main.tf +++ b/wrappers/service/main.tf @@ -138,6 +138,7 @@ module "wrapper" { triggers = try(each.value.triggers, var.defaults.triggers, null) volume = try(each.value.volume, var.defaults.volume, null) volume_configuration = try(each.value.volume_configuration, var.defaults.volume_configuration, null) + vpc_id = try(each.value.vpc_id, var.defaults.vpc_id, null) vpc_lattice_configurations = try(each.value.vpc_lattice_configurations, var.defaults.vpc_lattice_configurations, null) wait_for_steady_state = try(each.value.wait_for_steady_state, var.defaults.wait_for_steady_state, null) wait_until_stable = try(each.value.wait_until_stable, var.defaults.wait_until_stable, null)