diff --git a/README.md b/README.md index 799fd33..3d1f2ea 100644 --- a/README.md +++ b/README.md @@ -132,16 +132,16 @@ No resources. | Name | Description | Type | Default | Required | |------|-------------|------|---------|:--------:| -| [alb](#input\_alb) | Configuration settings for the Application Load Balancer (ALB). This includes attributes related to the ALB itself, such as its name, port, protocol, and other optional settings like access logs and tags. |
object({| n/a | yes | -| [alb\_target\_group](#input\_alb\_target\_group) | List of target groups to create |
name = optional(string, null)
port = optional(number)
protocol = optional(string, "HTTP")
internal = optional(bool, false)
load_balancer_type = optional(string, "application")
idle_timeout = optional(number, 60)
enable_deletion_protection = optional(bool, false)
enable_http2 = optional(bool, true)
certificate_arn = optional(string, null)
create_alb = optional(bool, false)
access_logs = optional(object({
bucket = string
enabled = optional(bool, false)
prefix = optional(string, "")
}))
tags = optional(map(string), {})
})
list(object({| n/a | yes | -| [capacity\_provider](#input\_capacity\_provider) | Configuration settings for the ECS capacity providers, including the capacity providers used for autoscaling and Fargate. This variable defines the properties of each capacity provider and how they are managed, such as scaling policies and termination protection. |
name = optional(string, "target-group")
port = number
protocol = optional(string, null)
protocol_version = optional(string, "HTTP1")
vpc_id = optional(string, "")
target_type = optional(string, "ip")
ip_address_type = optional(string, "ipv4")
load_balancing_algorithm_type = optional(string, "round_robin")
load_balancing_cross_zone_enabled = optional(string, "use_load_balancer_configuration")
deregistration_delay = optional(number, 300)
slow_start = optional(number, 0)
tags = optional(map(string), {})
health_check = optional(object({
enabled = optional(bool, true)
protocol = optional(string, "HTTP")
path = optional(string, "/")
port = optional(string, "traffic-port")
timeout = optional(number, 6)
healthy_threshold = optional(number, 3)
unhealthy_threshold = optional(number, 3)
interval = optional(number, 30)
matcher = optional(string, "200")
}))
stickiness = optional(object({
enabled = optional(bool, true)
type = string
cookie_duration = optional(number, 86400)
})
)
}))
object({| n/a | yes | -| [cidr\_blocks](#input\_cidr\_blocks) | CIDR blocks for security group ingress rules | `list(string)` |
autoscaling_capacity_providers = map(object({
name = optional(string)
auto_scaling_group_arn = string
managed_termination_protection = optional(string, "DISABLED")
managed_draining = optional(string, "ENABLED")
managed_scaling = optional(object({
instance_warmup_period = optional(number)
maximum_scaling_step_size = optional(number)
minimum_scaling_step_size = optional(number)
status = optional(string)
target_capacity = optional(number)
}))
tags = optional(map(string), {})
}))
use_fargate = bool
fargate_capacity_providers = any
})
[| no | -| [ecs\_cluster](#input\_ecs\_cluster) | The ECS-specific values to use such as cluster, service, and repository names.
"0.0.0.0/0"
]
object({| n/a | yes | -| [ecs\_service](#input\_ecs\_service) | The ECS-specific values to use such as cluster, service, and repository names. |
name = string
configuration = optional(object({
execute_command_configuration = optional(object({
kms_key_id = optional(string, "")
logging = optional(string, "DEFAULT")
log_configuration = optional(object({
cloudwatch_encryption_enabled = optional(bool, null)
log_group_name = optional(string, null)
log_group_retention_in_days = optional(number, null)
log_group_kms_key_id = optional(string, null)
log_group_tags = optional(map(string), null)
s3_bucket_name = optional(string, null)
s3_bucket_encryption_enabled = optional(bool, null)
s3_key_prefix = optional(string, null)
}), {})
}), {})
}), {})
create_cloudwatch_log_group = bool
service_connect_defaults = optional(map(string), null)
settings = optional(any, null)
tags = optional(map(string), null)
})
object({| n/a | yes | +| [alb](#input\_alb) | Configuration settings for the Application Load Balancer (ALB). This includes attributes related to the ALB itself, such as its name, port, protocol, and other optional settings like access logs and tags. |
cluster_name = string
service_name = string
repository_name = string
enable_load_balancer = bool
aws_lb_target_group_name = optional(string)
create_service = optional(bool, false)
})
object({| n/a | yes | +| [alb\_target\_group](#input\_alb\_target\_group) | List of target groups to create |
name = optional(string, null)
port = optional(number)
protocol = optional(string, "HTTP")
internal = optional(bool, false)
load_balancer_type = optional(string, "application")
idle_timeout = optional(number, 60)
enable_deletion_protection = optional(bool, false)
enable_http2 = optional(bool, true)
certificate_arn = optional(string, null)
create_alb = optional(bool, false)
access_logs = optional(object({
bucket = string
enabled = optional(bool, false)
prefix = optional(string, "")
}))
tags = optional(map(string), {})
})
list(object({| n/a | yes | +| [capacity\_provider](#input\_capacity\_provider) | Configuration settings for the ECS capacity providers, including the capacity providers used for autoscaling and Fargate. This variable defines the properties of each capacity provider and how they are managed, such as scaling policies and termination protection. |
name = optional(string, "target-group")
port = number
protocol = optional(string, null)
protocol_version = optional(string, "HTTP1")
vpc_id = optional(string, "")
target_type = optional(string, "ip")
ip_address_type = optional(string, "ipv4")
load_balancing_algorithm_type = optional(string, "round_robin")
load_balancing_cross_zone_enabled = optional(string, "use_load_balancer_configuration")
deregistration_delay = optional(number, 300)
slow_start = optional(number, 0)
tags = optional(map(string), {})
health_check = optional(object({
enabled = optional(bool, true)
protocol = optional(string, "HTTP")
path = optional(string, "/")
port = optional(string, "traffic-port")
timeout = optional(number, 6)
healthy_threshold = optional(number, 3)
unhealthy_threshold = optional(number, 3)
interval = optional(number, 30)
matcher = optional(string, "200")
}))
stickiness = optional(object({
enabled = optional(bool, true)
type = string
cookie_duration = optional(number, 86400)
})
)
}))
object({| n/a | yes | +| [cidr\_blocks](#input\_cidr\_blocks) | CIDR blocks for security group ingress rules | `list(string)` |
autoscaling_capacity_providers = map(object({
name = optional(string)
auto_scaling_group_arn = string
managed_termination_protection = optional(string, "DISABLED")
managed_draining = optional(string, "ENABLED")
managed_scaling = optional(object({
instance_warmup_period = optional(number)
maximum_scaling_step_size = optional(number)
minimum_scaling_step_size = optional(number)
status = optional(string)
target_capacity = optional(number)
}))
tags = optional(map(string), {})
}))
use_fargate = bool
fargate_capacity_providers = any
})
[| no | +| [ecs\_cluster](#input\_ecs\_cluster) | The ECS-specific values to use such as cluster, service, and repository names.
"0.0.0.0/0"
]
object({| n/a | yes | +| [ecs\_service](#input\_ecs\_service) | The ECS-specific values to use such as cluster, service, and repository names. |
name = string
configuration = optional(object({
execute_command_configuration = optional(object({
kms_key_id = optional(string, "")
logging = optional(string, "DEFAULT")
log_configuration = optional(object({
cloudwatch_encryption_enabled = optional(bool, null)
log_group_name = optional(string, null)
log_group_retention_in_days = optional(number, null)
log_group_kms_key_id = optional(string, null)
log_group_tags = optional(map(string), null)
s3_bucket_name = optional(string, null)
s3_bucket_encryption_enabled = optional(bool, null)
s3_key_prefix = optional(string, null)
}), {})
}), {})
}), {})
create_cloudwatch_log_group = bool
service_connect_defaults = optional(map(string), null)
settings = optional(any, null)
tags = optional(map(string), null)
})
object({| n/a | yes | | [environment](#input\_environment) | The environment associated with the ECS service | `string` | n/a | yes | -| [lb](#input\_lb) | ALB-related information (listening port, deletion protection, security group) |
cluster_name = string
service_name = string
repository_name = string
enable_load_balancer = bool
aws_lb_target_group_name = optional(string)
create_service = optional(bool, false)
})
object({| n/a | yes | -| [listener\_rules](#input\_listener\_rules) | List of listener rules to create |
name = string
listener_port = number
deregistration_delay = optional(number)
security_group_id = string
})
list(object({| n/a | yes | -| [task](#input\_task) | Task-related information (vCPU, memory, # of tasks, port, and health check info.) |
priority = number
conditions = list(object({
field = string
values = list(string)
}))
actions = list(object({
type = string
target_group_arn = optional(string)
order = optional(number)
redirect = optional(object({
protocol = string
port = string
host = optional(string)
path = optional(string)
query = optional(string)
status_code = string
}), null)
fixed_response = optional(object({
content_type = string
message_body = optional(string)
status_code = optional(string)
}), null)
}))
}))
object({| n/a | yes | +| [lb](#input\_lb) | ALB-related information (listening port, deletion protection, security group) |
tasks_desired = optional(number)
container_vcpu = optional(number)
container_memory = optional(number)
container_port = number
container_health_check_path = optional(string)
container_definition = optional(string)
environment_variables = optional(map(string))
task_execution_role = optional(string)
})
object({| n/a | yes | +| [listener\_rules](#input\_listener\_rules) | List of listener rules to create |
name = string
listener_port = number
deregistration_delay = optional(number)
security_group_id = string
})
list(object({| n/a | yes | +| [task](#input\_task) | Task-related information (vCPU, memory, # of tasks, port, and health check info.) |
priority = number
conditions = list(object({
field = string
values = list(string)
}))
actions = list(object({
type = string
target_group_arn = optional(string)
order = optional(number)
redirect = optional(object({
protocol = string
port = string
host = optional(string)
path = optional(string)
query = optional(string)
status_code = string
}), null)
fixed_response = optional(object({
content_type = string
message_body = optional(string)
status_code = optional(string)
}), null)
}))
}))
object({| n/a | yes | | [vpc\_id](#input\_vpc\_id) | ID of VPC in which all resources need to be created | `string` | n/a | yes | ## Outputs diff --git a/main.tf b/main.tf index 795109e..dbba6da 100644 --- a/main.tf +++ b/main.tf @@ -18,6 +18,15 @@ module "ecs_cluster" { use_fargate = var.capacity_provider.use_fargate fargate_capacity_providers = var.capacity_provider.fargate_capacity_providers } + + launch_template = { + name = "lt1" + } + + asg = { + max_size = 2 + min_size = 1 + } } diff --git a/modules/ecs_cluster/main.tf b/modules/ecs_cluster/main.tf index 71286be..a32b589 100644 --- a/modules/ecs_cluster/main.tf +++ b/modules/ecs_cluster/main.tf @@ -81,17 +81,142 @@ resource "aws_ecs_cluster" "this" { tags = merge(var.tags, var.ecs_cluster.tags) } +################################################################################ +# EC2 Launch Template +################################################################################ + +resource "aws_launch_template" "this" { + count = var.capacity_provider.use_fargate != true ? 1 : 0 + + name = var.launch_template.name + + dynamic "block_device_mappings" { + for_each = var.launch_template.block_device_mappings + content { + device_name = block_device_mappings.value.device_name + + dynamic "ebs" { + for_each = block_device_mappings.value.ebs != null ? [block_device_mappings.value.ebs] : [] + content { + volume_size = ebs.value.volume_size + } + } + } + } + + dynamic "cpu_options" { + for_each = var.launch_template.cpu_options != null ? [var.launch_template.cpu_options] : [] + content { + core_count = cpu_options.value.core_count + threads_per_core = cpu_options.value.threads_per_core + } + } + + disable_api_stop = var.launch_template.disable_api_stop + disable_api_termination = var.launch_template.disable_api_termination + ebs_optimized = var.launch_template.ebs_optimized + + dynamic "elastic_gpu_specifications" { + for_each = var.launch_template.elastic_gpu_specifications + content { + type = elastic_gpu_specifications.value.type + } + } + + dynamic "iam_instance_profile" { + for_each = var.launch_template.iam_instance_profile != null ? [var.launch_template.iam_instance_profile] : [] + content { + name = iam_instance_profile.value.name + } + } + + image_id = var.launch_template.image_id + + instance_initiated_shutdown_behavior = var.launch_template.instance_initiated_shutdown_behavior + + dynamic "monitoring" { + for_each = var.launch_template.monitoring != null ? [var.launch_template.monitoring] : [] + content { + enabled = monitoring.value.enabled + } + } + + dynamic "network_interfaces" { + for_each = var.launch_template.network_interfaces + content { + associate_public_ip_address = network_interfaces.value.associate_public_ip_address + ipv4_prefixes = network_interfaces.value.ipv4_prefixes + ipv6_prefixes = network_interfaces.value.ipv6_prefixes + ipv4_addresses = network_interfaces.value.ipv4_addresses + ipv6_addresses = network_interfaces.value.ipv6_addresses + network_interface_id = network_interfaces.value.network_interface_id + private_ip_address = network_interfaces.value.private_ip_address + security_groups = network_interfaces.value.security_groups + subnet_id = network_interfaces.value.subnet_id + } + } + + dynamic "placement" { + for_each = var.launch_template.placement != null ? [var.launch_template.placement] : [] + content { + availability_zone = placement.value.availability_zone + } + } + + vpc_security_group_ids = var.launch_template.vpc_security_group_ids + + dynamic "tag_specifications" { + for_each = var.launch_template.tag_specifications + content { + resource_type = tag_specifications.value.resource_type + tags = tag_specifications.value.tags + } + } + + user_data = var.launch_template.user_data != null ? filebase64(var.launch_template.user_data) : null +} + +################################################################################ +# Auto Scaling Group +################################################################################ + +resource "aws_autoscaling_group" "this" { + count = var.capacity_provider.use_fargate != true ? 1 : 0 + + name = var.asg.name != null ? var.asg.name : "ecs-auto-scaling-group" + min_size = var.asg.min_size + max_size = var.asg.max_size + desired_capacity = var.asg.desired_capacity != null ? var.asg.desired_capacity : var.asg.min_size + vpc_zone_identifier = var.asg.vpc_zone_identifier != null ? var.asg.vpc_zone_identifier : [] + + launch_template { + id = aws_launch_template.this[0].id + } + + health_check_type = var.asg.health_check_type != null ? var.asg.health_check_type : "EC2" + health_check_grace_period = var.asg.health_check_grace_period != null ? var.asg.health_check_grace_period : 300 + protect_from_scale_in = var.asg.protect_from_scale_in != null ? var.asg.protect_from_scale_in : false + default_cooldown = var.asg.default_cooldown != null ? var.asg.default_cooldown : 300 + + instance_refresh { + strategy = var.asg.instance_refresh.strategy + preferences { + min_healthy_percentage = var.asg.instance_refresh.preferences != null && var.asg.instance_refresh.preferences.min_healthy_percentage != null ? var.asg.instance_refresh.preferences.min_healthy_percentage : 50 + } + } +} + ################################################################################ # ECS Capacity Provider - EC2 ################################################################################ resource "aws_ecs_capacity_provider" "this" { - for_each = var.capacity_provider.autoscaling_capacity_providers != null ? var.capacity_provider.autoscaling_capacity_providers : {} + for_each = (var.capacity_provider.use_fargate != true && var.capacity_provider.autoscaling_capacity_providers != null) ? var.capacity_provider.autoscaling_capacity_providers : {} name = each.value.name != "" ? each.value.name : each.key auto_scaling_group_provider { - auto_scaling_group_arn = each.value.auto_scaling_group_arn + auto_scaling_group_arn = aws_autoscaling_group.this[0].arn # Enable managed termination protection only if managed scaling is defined managed_termination_protection = each.value.managed_scaling != null ? (each.value.managed_termination_protection != null ? each.value.managed_termination_protection : "DISABLED") : "DISABLED" diff --git a/modules/ecs_cluster/variables.tf b/modules/ecs_cluster/variables.tf index e21be67..7da391f 100644 --- a/modules/ecs_cluster/variables.tf +++ b/modules/ecs_cluster/variables.tf @@ -56,7 +56,93 @@ EOT } } +variable "launch_template" { + type = object({ + name = string + block_device_mappings = optional(list(object({ + device_name = string + ebs = optional(object({ + volume_size = number + })) + })), []) + + cpu_options = optional(object({ + core_count = number + threads_per_core = number + }), null) + + disable_api_stop = optional(bool, false) + disable_api_termination = optional(bool, false) + ebs_optimized = optional(bool, false) + + elastic_gpu_specifications = optional(list(object({ + type = string + })), []) + + iam_instance_profile = optional(object({ + name = string + }), null) + + image_id = optional(string, null) + instance_initiated_shutdown_behavior = optional(string, "stop") + + instance_type = optional(string, null) + kernel_id = optional(string, null) + key_name = optional(string, null) + + monitoring = optional(object({ + enabled = bool + }), null) + + network_interfaces = optional(list(object({ + associate_public_ip_address = optional(bool, null) + ipv4_prefixes = optional(list(string), []) + ipv6_prefixes = optional(list(string), []) + ipv4_addresses = optional(list(string), []) + ipv6_addresses = optional(list(string), []) + network_interface_id = optional(string, null) + private_ip_address = optional(string, null) + security_groups = optional(list(string), []) + subnet_id = optional(string, null) + })), []) + + placement = optional(object({ + availability_zone = string + }), null) + vpc_security_group_ids = optional(list(string), []) + + tag_specifications = optional(list(object({ + resource_type = string + tags = map(string) + })), []) + + user_data = optional(string, null) + }) +} + +variable "asg" { + description = "Auto Scaling Group configuration" + type = object({ + name = optional(string, null) + min_size = number + max_size = number + desired_capacity = optional(number) + vpc_zone_identifier = optional(list(string)) + + health_check_type = optional(string) + health_check_grace_period = optional(number, 300) + protect_from_scale_in = optional(bool) + default_cooldown = optional(number) + + instance_refresh = optional(object({ + strategy = string + preferences = optional(object({ + min_healthy_percentage = optional(number) + })) + })) + }) +} ################################################################################ # Cluster Capacity Providers diff --git a/variables.tf b/variables.tf index b2462a7..d1b9a87 100644 --- a/variables.tf +++ b/variables.tf @@ -62,6 +62,95 @@ variable "capacity_provider" { } +/* variable "launch_template" { + type = object({ + name = string + block_device_mappings = optional(list(object({ + device_name = string + ebs = optional(object({ + volume_size = number + })) + })), []) + + cpu_options = optional(object({ + core_count = number + threads_per_core = number + }), null) + + disable_api_stop = optional(bool, false) + disable_api_termination = optional(bool, false) + ebs_optimized = optional(bool, false) + + elastic_gpu_specifications = optional(list(object({ + type = string + })), []) + + iam_instance_profile = optional(object({ + name = string + }), null) + + image_id = optional(string, null) + instance_initiated_shutdown_behavior = optional(string, "stop") + + instance_type = optional(string, null) + kernel_id = optional(string, null) + key_name = optional(string, null) + + monitoring = optional(object({ + enabled = bool + }), null) + + network_interfaces = optional(list(object({ + associate_public_ip_address = optional(bool, null) + ipv4_prefixes = optional(list(string), []) + ipv6_prefixes = optional(list(string), []) + ipv4_addresses = optional(list(string), []) + ipv6_addresses = optional(list(string), []) + network_interface_id = optional(string, null) + private_ip_address = optional(string, null) + security_groups = optional(list(string), []) + subnet_id = optional(string, null) + })), []) + + placement = optional(object({ + availability_zone = string + }), null) + + vpc_security_group_ids = optional(list(string), []) + + tag_specifications = optional(list(object({ + resource_type = string + tags = map(string) + })), []) + + user_data = optional(string, null) + }) +} + +variable "asg" { + description = "Auto Scaling Group configuration" + type = object({ + name = optional(string, null) + min_size = number + max_size = number + desired_capacity = optional(number) + vpc_zone_identifier = optional(list(string)) + + health_check_type = optional(string) + health_check_grace_period = optional(number, 300) + protect_from_scale_in = optional(bool) + default_cooldown = optional(number) + + instance_refresh = object({ + strategy = string + preferences = optional(object({ + min_healthy_percentage = optional(number) + })) + }) + }) +} */ + + ################################################################################ ## ALB ################################################################################
tasks_desired = optional(number)
container_vcpu = optional(number)
container_memory = optional(number)
container_port = number
container_health_check_path = optional(string)
container_definition = optional(string)
environment_variables = optional(map(string))
task_execution_role = optional(string)
})