diff --git a/README.md b/README.md
index f6688cf..5aa7732 100644
--- a/README.md
+++ b/README.md
@@ -41,6 +41,7 @@ No modules.
| [certificate\_arn](#input\_certificate\_arn) | ARN of an existing SSL certificate for HTTPS | `string` | `""` | no |
| [enable\_deletion\_protection](#input\_enable\_deletion\_protection) | Enable or disable deletion protection for the ALB | `bool` | `false` | no |
| [enable\_https](#input\_enable\_https) | Enable HTTPS listener (must provide a certificate ARN) | `bool` | `false` | no |
+| [enable\_availability\_zone\_all](#input\_enable_availability_zone_all) | Set availability_zone to 'all' for IP targets outside VPC | `bool` | `false` | no |
| [health\_check\_healthy\_threshold](#input\_health\_check\_healthy\_threshold) | Number of successful health checks before considering the target healthy | `number` | `3` | no |
| [health\_check\_interval](#input\_health\_check\_interval) | Health check interval in seconds | `number` | `30` | no |
| [health\_check\_path](#input\_health\_check\_path) | The health check endpoint for ALB target group | `string` | `"/"` | no |
diff --git a/example/http-fargate/.terraform.lock.hcl b/example/http-fargate/.terraform.lock.hcl
new file mode 100644
index 0000000..92cb2bb
--- /dev/null
+++ b/example/http-fargate/.terraform.lock.hcl
@@ -0,0 +1,63 @@
+# This file is maintained automatically by "terraform init".
+# Manual edits may be lost in future updates.
+
+provider "registry.terraform.io/hashicorp/aws" {
+ version = "5.84.0"
+ constraints = ">= 3.29.0, 5.84.0"
+ hashes = [
+ "h1:dwpeFUdcxgXVAc0JSqO57xf0/r2qOBLPloombCQWFz8=",
+ "zh:078f77438aba6ec8bf9154b7d223e5c71c48d805d6cd3bcf9db0cc1e82668ac3",
+ "zh:1f6591ff96be00501e71b792ed3a5a14b21ff03afec9a1c4a3fd9300e6e5b674",
+ "zh:2ab694e022e81dd74485351c5836148a842ed71cf640664c9d871cb517b09602",
+ "zh:33c8ccb6e3dc496e828a7572dd981366c6271075c1189f249b9b5236361d7eff",
+ "zh:6f31068ebad1d627e421c72ccdaafe678c53600ca73714e977bf45ff43ae5d17",
+ "zh:7488623dccfb639347cae66f9001d39cf06b92e8081975235a1ac3a0ac3f44aa",
+ "zh:7f042b78b9690a8725c95b91a70fc8e264011b836605bcc342ac297b9ea3937d",
+ "zh:88b56ac6c7209dc0a775b79975a371918f3aed8f015c37d5899f31deff37c61a",
+ "zh:9b12af85486a96aedd8d7984b0ff811a4b42e3d88dad1a3fb4c0b580d04fa425",
+ "zh:a1979ba840d704af0932f8de5f541cbb4caa9b6bbd25ed552a24e6772175ba07",
+ "zh:b058c0533dae580e69d1adbc1f69e6a80632374abfc10e8634d06187a108e87b",
+ "zh:c88610af9cf957f8dcf4382e0c9ca566ef10e3290f5de01d4d90b2d81b078aa8",
+ "zh:e9562c055a2247d0c287772b55abef468c79f8d66a74780fe1c5e5dae1a284a9",
+ "zh:f7a7c71d28441d925a25c08c4485c015b2d9f0338bc9707443e91ff8e161d3d9",
+ "zh:fee533e81976d0900aa6fa443dc54ef171cbd901847f28a6e8edb1d161fa6fde",
+ ]
+}
+
+provider "registry.terraform.io/hashicorp/http" {
+ version = "3.4.5"
+ hashes = [
+ "h1:ceAVZEuaQd7jQX13qf5w7hy3ioiXpuwUaaDRsnAiMLM=",
+ "zh:2072006c177efc101471f3d5eb8e1d8e6c68778cbfd6db3d3f22f59cfe6ce6ae",
+ "zh:3ac4cc0efe11ee054300769cfcc37491433937a8824621d1f8f7a18e7401da87",
+ "zh:63997e5457c9ddf9cfff17bd7bf9f083cbeff3105452045662109dd6be499ef9",
+ "zh:78d5eefdd9e494defcb3c68d282b8f96630502cac21d1ea161f53cfe9bb483b3",
+ "zh:826819bb8ab7d6e3095f597083d5b1ab93d1854312b9e1b6c18288fff9664f34",
+ "zh:8ad74e7d8ec2e226a73d49c7c317108f61a4cb803972fb3f945d1709d5115fcd",
+ "zh:a609ca9e0c91d250ac80295e39d5f524e8c0872d33ba8fde3c3e41893b4b015d",
+ "zh:ae07d19babc452f63f6a6511b944990e819dc20687b6c8f01d1676812f5ada53",
+ "zh:b7c827dc32a1a5d77185a78cd391b01217894b384f58169f98a96d683730d8ce",
+ "zh:d045e3db9f5e39ce78860d3fd94e04604fcbe246f6fe346ee50a971f936e9ccd",
+ "zh:ec28f9b52c74edd47eebbb5c254a6df5706360cde5ccd65097976efca23a2977",
+ "zh:f24982eaa7d34fd66554c3cf94873713a0dff14da9ea4c4be0cc76f1a6146d59",
+ ]
+}
+
+provider "registry.terraform.io/hashicorp/random" {
+ version = "3.7.1"
+ hashes = [
+ "h1:/qtweZW2sk0kBNiQM02RvBXmlVdI9oYqRMCyBZ8XA98=",
+ "zh:3193b89b43bf5805493e290374cdda5132578de6535f8009547c8b5d7a351585",
+ "zh:3218320de4be943e5812ed3de995946056db86eb8d03aa3f074e0c7316599bef",
+ "zh:419861805a37fa443e7d63b69fb3279926ccf98a79d256c422d5d82f0f387d1d",
+ "zh:4df9bd9d839b8fc11a3b8098a604b9b46e2235eb65ef15f4432bde0e175f9ca6",
+ "zh:5814be3f9c9cc39d2955d6f083bae793050d75c572e70ca11ccceb5517ced6b1",
+ "zh:63c6548a06de1231c8ee5570e42ca09c4b3db336578ded39b938f2156f06dd2e",
+ "zh:697e434c6bdee0502cc3deb098263b8dcd63948e8a96d61722811628dce2eba1",
+ "zh:78d5eefdd9e494defcb3c68d282b8f96630502cac21d1ea161f53cfe9bb483b3",
+ "zh:a0b8e44927e6327852bbfdc9d408d802569367f1e22a95bcdd7181b1c3b07601",
+ "zh:b7d3af018683ef22794eea9c218bc72d7c35a2b3ede9233b69653b3c782ee436",
+ "zh:d63b911d618a6fe446c65bfc21e793a7663e934b2fef833d42d3ccd38dd8d68d",
+ "zh:fa985cd0b11e6d651f47cff3055f0a9fd085ec190b6dbe99bf5448174434cdea",
+ ]
+}
diff --git a/example/http-fargate/main.tf b/example/http-fargate/main.tf
new file mode 100644
index 0000000..83a2da4
--- /dev/null
+++ b/example/http-fargate/main.tf
@@ -0,0 +1,238 @@
+############################################
+# Provider Configuration
+############################################
+
+terraform {
+ required_providers {
+ aws = {
+ source = "hashicorp/aws"
+ version = "5.84.0"
+ }
+ }
+}
+
+provider "aws" {
+ region = "ap-southeast-1"
+}
+
+############################################
+# Data Sources
+############################################
+
+data "aws_region" "current" {}
+data "aws_availability_zones" "available" {}
+
+data "http" "my_public_ip" {
+ url = "http://ifconfig.me/ip"
+}
+
+############################################
+# Random Suffix for Resource Names
+############################################
+
+resource "random_string" "suffix" {
+ length = 8
+ special = false
+ upper = false
+}
+
+############################################
+# Local Variables
+############################################
+
+locals {
+ name = "cltest"
+ base_name = "${local.name}-${random_string.suffix.result}"
+
+ tags = {
+ Environment = "dev"
+ Project = "example"
+ }
+}
+
+############################################
+# VPC Configuration
+############################################
+
+module "vpc" {
+ source = "tfstack/vpc/aws"
+
+ vpc_name = local.base_name
+ vpc_cidr = "10.0.0.0/16"
+ availability_zones = slice(data.aws_availability_zones.available.names, 0, 3)
+
+ public_subnets = ["10.0.1.0/24", "10.0.2.0/24", "10.0.3.0/24"]
+ private_subnets = ["10.0.101.0/24", "10.0.102.0/24", "10.0.103.0/24"]
+
+ eic_subnet = "none"
+ eic_ingress_cidrs = ["${data.http.my_public_ip.response_body}/32"]
+
+ jumphost_instance_create = false
+ jumphost_subnet = "10.0.0.0/24"
+ jumphost_log_prevent_destroy = false
+ create_igw = true
+ ngw_type = "single"
+
+ tags = local.tags
+}
+
+############################################
+# AWS ALB Module
+############################################
+
+module "aws_alb" {
+ source = "../.."
+
+ name = local.base_name
+ suffix = random_string.suffix.result
+ vpc_id = module.vpc.vpc_id
+ public_subnet_ids = module.vpc.public_subnet_ids
+
+ enable_https = false
+ http_port = 80
+ target_http_port = 80
+ target_type = "ip"
+
+ public_subnet_cidrs = module.vpc.public_subnet_cidrs
+}
+
+############################################
+# ECS Cluster Configuration
+############################################
+
+module "ecs_cluster_fargate" {
+ source = "tfstack/ecs-cluster-fargate/aws"
+
+ # Core Configuration
+ cluster_name = local.name
+ suffix = random_string.suffix.result
+
+ # VPC Configuration
+ vpc = {
+ id = module.vpc.vpc_id
+ private_subnets = [
+ for i, subnet in module.vpc.private_subnet_ids :
+ { id = subnet, cidr = module.vpc.private_subnet_cidrs[i] }
+ ]
+ public_subnets = [
+ for i, subnet in module.vpc.public_subnet_ids :
+ { id = subnet, cidr = module.vpc.public_subnet_cidrs[i] }
+ ]
+ }
+
+ # Cluster Settings
+ cluster_settings = [
+ { name = "containerInsights", value = "enabled" }
+ ]
+
+ # Logging Configuration
+ s3_key_prefix = "logs/"
+ create_cloudwatch_log_group = true
+ cloudwatch_log_group_retention_days = 90
+ create_s3_logging_bucket = true
+
+ # Capacity Providers
+ capacity_providers = {
+ FARGATE = {
+ default_capacity_provider_strategy = {
+ weight = 50
+ base = 20
+ }
+ }
+ FARGATE_SPOT = {
+ default_capacity_provider_strategy = {
+ weight = 50
+ }
+ }
+ }
+
+ ecs_services = [
+ {
+ name = "web-app"
+ desired_count = 3
+ cpu = "256"
+ memory = "512"
+ force_new_deployment = true
+
+ execution_role_policies = [
+ "arn:aws:iam::aws:policy/AmazonS3ReadOnlyAccess",
+ "arn:aws:iam::aws:policy/CloudWatchLogsFullAccess"
+ ]
+
+ container_definitions = jsonencode([
+ {
+ name = "web-app"
+ image = "nginx:latest"
+ cpu = 256
+ memory = 512
+ essential = true
+ portMappings = [{
+ containerPort = 80
+ }]
+ healthCheck = {
+ command = ["CMD-SHELL", "curl -f http://127.0.0.1 || exit 1"]
+ interval = 30
+ timeout = 5
+ retries = 3
+ startPeriod = 10
+ }
+ logConfiguration = {
+ logDriver = "awslogs"
+ options = {
+ awslogs-group = "/aws/ecs/${local.name}-web-app"
+ awslogs-region = data.aws_region.current.name
+ awslogs-stream-prefix = "${local.name}-nginx"
+ }
+ }
+ }
+ ])
+
+ deployment_minimum_healthy_percent = 100
+ deployment_maximum_percent = 200
+ health_check_grace_period_seconds = 30
+
+ subnet_ids = module.vpc.private_subnet_ids
+ security_groups = [aws_security_group.ecs.id]
+ assign_public_ip = false
+
+ enable_alb = true
+ enable_ecs_managed_tags = true
+ propagate_tags = "TASK_DEFINITION"
+
+ service_tags = {
+ Environment = "staging"
+ Project = "WebApp"
+ Owner = "DevOps"
+ }
+
+ task_tags = {
+ TaskType = "backend"
+ Version = "1.0"
+ }
+ }
+ ]
+
+ ecs_autoscaling = [
+ {
+ service_name = "${local.name}-web-app"
+ min_capacity = 3
+ max_capacity = 12
+ scalable_dimension = "ecs:service:DesiredCount"
+ policy_name = "scale-on-cpu"
+ policy_type = "TargetTrackingScaling"
+ target_value = 75
+ predefined_metric_type = "ECSServiceAverageCPUUtilization"
+ }
+ ]
+
+ tags = local.tags
+}
+
+############################################
+# Outputs
+############################################
+
+output "all_module_outputs" {
+ description = "All outputs from the ALB module"
+ value = module.aws_alb
+}
diff --git a/main.tf b/main.tf
index c47156c..88861a7 100644
--- a/main.tf
+++ b/main.tf
@@ -66,10 +66,11 @@ resource "aws_security_group" "this" {
resource "aws_lb_target_group" "http" {
count = var.enable_https ? 0 : 1
- name = "${local.base_name}-http"
- port = var.target_http_port
- protocol = "HTTP" # var.target_protocol
- vpc_id = var.vpc_id
+ name = "${local.base_name}-http"
+ port = var.target_http_port
+ protocol = "HTTP" # var.target_protocol
+ target_type = var.target_type
+ vpc_id = var.vpc_id
health_check {
path = var.health_check_path
@@ -84,11 +85,12 @@ resource "aws_lb_target_group" "http" {
# HTTPS Target Group (Only created if HTTPS is enabled)
resource "aws_lb_target_group" "https" {
- count = var.enable_https ? 1 : 0
- name = "${local.base_name}-https"
- port = var.target_https_port
- protocol = "HTTPS" # var.target_protocol
- vpc_id = var.vpc_id
+ count = var.enable_https ? 1 : 0
+ name = "${local.base_name}-https"
+ port = var.target_https_port
+ protocol = "HTTPS" # var.target_protocol
+ target_type = var.target_type
+ vpc_id = var.vpc_id
health_check {
path = var.health_check_path
@@ -170,8 +172,8 @@ resource "aws_lb_target_group_attachment" "generic" {
target_id = each.value
# Ensure port is only applied for instance and IP targets
- port = var.target_type == "instance" || var.target_type == "ip" ? var.target_http_port : null
+ port = contains(["instance", "ip"], var.target_type) ? var.target_http_port : null
# Ensure availability zone is applied only for IP targets outside VPC
- availability_zone = var.target_type == "ip" && !contains(var.public_subnet_cidrs, each.value) ? "all" : null
+ availability_zone = var.enable_availability_zone_all ? "all" : null
}
diff --git a/variables.tf b/variables.tf
index fe12082..e9ee21d 100644
--- a/variables.tf
+++ b/variables.tf
@@ -145,10 +145,11 @@ variable "certificate_arn" {
variable "targets" {
description = "List of targets (EC2 instance IDs, IPs, Lambda ARNs, or ALB ARNs)"
type = list(string)
+ default = []
validation {
- condition = length(var.targets) > 0
- error_message = "At least one target must be specified."
+ condition = var.target_type == "ip" || length(var.targets) > 0
+ error_message = "At least one target must be specified unless target_type is 'ip'."
}
}
@@ -162,3 +163,9 @@ variable "target_type" {
error_message = "Allowed values for target_type are 'instance', 'ip', 'lambda', or 'alb'."
}
}
+
+variable "enable_availability_zone_all" {
+ description = "Set availability_zone to 'all' for IP targets outside VPC"
+ type = bool
+ default = false
+}