Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ This repository contains reusable infrastructure modules designed for enterprise
| `compute/` | `autoscaling` | AWS Auto Scaling groups | v1.0.0 |
| `compute/` | `ec2` | AWS EC2 instances | Planned |
| `compute/` | `ecs_cluster` | AWS ECS clusters with Fargate/EC2 capacity providers and optional ALBs/NLBs | v1.0.0 |
| `compute/` | `ecs_service` | AWS ECS services with task definitions, task IAM policies, load balancing, and auto scaling | v1.0.0 |
| `compute/` | `ecs_service` | AWS ECS services with task definitions, task IAM policies, load balancing, and high-resolution auto scaling | v1.0.0 |
| `compute/` | `lambda` | AWS Lambda functions | v1.0.0 |
| `database/` | `aurora` | AWS Aurora clusters (MySQL, PostgreSQL, Serverless v2, Global Database) (includes `rvn-aurora` module definition) | v1.0.0 |
| `database/` | `dynamodb` | AWS DynamoDB tables | v1.0.0 |
Expand Down
11 changes: 9 additions & 2 deletions compute/ecs_service/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ When a load balancer is attached, the module always provisions the production +
- Listener rule configuration for path-based and host-based routing
- NLB listener creation with TLS support
- Application Auto Scaling with target tracking and scheduled scaling
- Optional 20-second CPU and memory metrics for faster target-tracking response
- AWS Cloud Map service discovery integration
- Native traffic-shift deployment infrastructure (production/alternate target groups, ECS infrastructure role, advanced_configuration) provisioned for every load-balanced service so the strategy can change per deployment
- Support for EFS and Docker volume configurations
Expand Down Expand Up @@ -253,10 +254,12 @@ module "worker_service" {

## Requirements

High-resolution ECS service monitoring requires the first AWS provider release containing [hashicorp/terraform-provider-aws#48792](https://github.com/hashicorp/terraform-provider-aws/pull/48792). Until that release is available, this implementation is intended for development review only. The Terraform runner role must also allow `ecs:DescribeServiceRevisions`, which the provider uses to read the service monitoring configuration.

| Name | Version |
|------|---------|
| opentofu/terraform | >= 1.10.0 |
| aws | >= 6.21 |
| aws | >= 6.21; high-resolution monitoring requires the first compatible release |

## Inputs

Expand Down Expand Up @@ -366,7 +369,7 @@ The `load_balancer_attachment` object includes:
The `auto_scaling` object includes:
- `enabled` - Enable auto scaling (default: true)
- `min_capacity` / `max_capacity` - Capacity limits
- `target_tracking` - List of target tracking policies (predefined or custom metrics)
- `target_tracking` - List of target tracking policies (predefined or custom metrics). The high-resolution CPU and memory predefined metrics enable 20-second ECS service monitoring and force Terraform to wait for the monitoring deployment to reach steady state before updating scaling policies.
- `scheduled` - List of scheduled scaling actions with cron expressions

### Service Discovery
Expand Down Expand Up @@ -942,9 +945,13 @@ The module supports these predefined ECS metrics:
| Metric | Description |
|--------|-------------|
| `ECSServiceAverageCPUUtilization` | Average CPU utilization across all tasks |
| `ECSServiceAverageCPUUtilizationHighResolution` | Average CPU utilization published every 20 seconds; adds CloudWatch charges |
| `ECSServiceAverageMemoryUtilization` | Average memory utilization across all tasks |
| `ECSServiceAverageMemoryUtilizationHighResolution` | Average memory utilization published every 20 seconds; adds CloudWatch charges |
| `ALBRequestCountPerTarget` | Average request count per target (requires load balancer) |

Selecting either high-resolution metric automatically configures ECS service monitoring for the corresponding `CPUUtilization` or `MemoryUtilization` metric. Selecting both publishes both metrics in one 20-second monitoring configuration. Standard predefined metrics and custom metrics omit ECS monitoring and retain their existing behavior.

Example with multiple target tracking policies:

```hcl
Expand Down
5 changes: 4 additions & 1 deletion compute/ecs_service/auto_scaling.tf
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,10 @@ resource "aws_appautoscaling_policy" "target_tracking" {
scalable_dimension = aws_appautoscaling_target.this[0].scalable_dimension
service_namespace = aws_appautoscaling_target.this[0].service_namespace

# When high-resolution monitoring changes, wait_for_steady_state keeps this
# policy update behind the completed ECS monitoring deployment.
depends_on = [aws_ecs_service.this]

target_tracking_scaling_policy_configuration {
target_value = each.value.target_value
scale_in_cooldown = each.value.scale_in_cooldown
Expand Down Expand Up @@ -98,4 +102,3 @@ resource "aws_appautoscaling_scheduled_action" "this" {
locals {
primary_target_group_arn_suffix = local.enable_load_balancer ? aws_lb_target_group.tg_1[0].arn_suffix : ""
}

14 changes: 13 additions & 1 deletion compute/ecs_service/ecs_service.tf
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,18 @@ resource "aws_ecs_service" "this" {
# Platform version for Fargate
platform_version = var.launch_type == "FARGATE" ? var.platform_version : null

# Publish 20-second ECS service metrics only when an active target-tracking
# policy uses one of the high-resolution predefined metric variants.
dynamic "monitoring" {
for_each = local.high_resolution_metrics_enabled ? [1] : []
content {
metric_configuration {
metric_names = local.high_resolution_metric_names
resolution_seconds = 20
}
}
}

# Network configuration (required for awsvpc network mode)
dynamic "network_configuration" {
for_each = var.network_mode == "awsvpc" ? [1] : []
Expand Down Expand Up @@ -130,7 +142,7 @@ resource "aws_ecs_service" "this" {
force_new_deployment = var.new_deployment_forcing_enabled

# Wait for steady state
wait_for_steady_state = var.steady_state_wait_enabled
wait_for_steady_state = var.steady_state_wait_enabled || local.high_resolution_metrics_enabled

# Tags
enable_ecs_managed_tags = var.ecs_managed_tags_enabled
Expand Down
7 changes: 7 additions & 0 deletions compute/ecs_service/locals.tf
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,13 @@ locals {

# Auto scaling settings
auto_scaling_enabled = var.auto_scaling != null && var.auto_scaling.enabled
high_resolution_metric_names = local.auto_scaling_enabled ? distinct(compact([
for policy in var.auto_scaling.target_tracking :
policy.predefined_metric == "ECSServiceAverageCPUUtilizationHighResolution" ? "CPUUtilization" :
policy.predefined_metric == "ECSServiceAverageMemoryUtilizationHighResolution" ? "MemoryUtilization" :
null
])) : []
high_resolution_metrics_enabled = length(local.high_resolution_metric_names) > 0

# Service discovery settings
enable_service_discovery = var.service_discovery != null
Expand Down
8 changes: 5 additions & 3 deletions compute/ecs_service/rvn-ecs-nlb-definition.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ definition:
name: ECS Network Service
description: Network Load Balanced ECS service for running TCP, UDP, or TLS workloads behind an ECS cluster Network Load Balancer.
release:
version: 0.2.2
description: "Accept mixed IAM policy document shapes for task role inline policies."
version: 1.0.0
description: "Enable high-resolution ECS autoscaling metrics by default."
module:
inputs:
- id: section_cluster
Expand Down Expand Up @@ -682,14 +682,15 @@ module:

## Autoscaling

Autoscaling is enabled by default with one minimum task and three maximum tasks. For production, start with at least two minimum tasks when availability matters. When autoscaling is disabled, Desired tasks controls how many tasks the ECS service keeps running.
Autoscaling is enabled by default with one minimum task and three maximum tasks. High-resolution scaling metrics are also enabled by default, publishing the built-in CPU and configured memory metrics every 20 seconds so target tracking can react faster; this adds CloudWatch charges. Turn off High-resolution scaling metrics to use the standard 60-second policies. For production, start with at least two minimum tasks when availability matters. When autoscaling is disabled, Desired tasks controls how many tasks the ECS service keeps running.

| Field | Default | Description |
| ------------------------- | ------- | --------------------------------------------- |
| Autoscaling | true | Enables ECS service target tracking |
| Minimum tasks | 1 | Lower bound for running tasks |
| Maximum tasks | 3 | Upper bound for running tasks |
| Desired tasks | 1 | Running tasks when autoscaling is disabled |
| High-resolution scaling metrics | true | Uses 20-second CPU and memory scaling metrics |
| CPU target (%) | 70 | Average CPU utilization target |
| Memory target (%) | 80 | Average memory utilization target |
| Scale-in cooldown (secs) | 300 | Delay before another scale-in action |
Expand Down Expand Up @@ -769,6 +770,7 @@ module:
| CPU architecture | No | X86_64 | x86_64 compatibility or ARM64 cost optimization |
| ECS exec | No | false | Enable ECS Exec for debugging containers |
| Autoscaling | No | true | Enable CPU and optional memory target tracking |
| High-resolution scaling metrics | No | true | Use 20-second CPU and memory scaling metrics |
| Desired tasks | Yes* | 1 | Number of tasks when autoscaling is disabled |
| Deployment strategy | Yes | rolling | Rolling, Blue/green, Linear, or Canary |
| FireLens log routing | No | false | Route app logs through a Fluent Bit sidecar |
Expand Down
8 changes: 5 additions & 3 deletions compute/ecs_service/rvn-ecs-web-definition.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ definition:
name: ECS Web Service
description: Web server ECS service for running an HTTP application behind an ECS cluster load balancer.
release:
version: 0.8.2
description: "Accept mixed IAM policy document shapes for task role inline policies."
version: 1.0.0
description: "Enable high-resolution ECS autoscaling metrics by default."
module:
inputs:
- id: section_cluster
Expand Down Expand Up @@ -663,14 +663,15 @@ module:

## Autoscaling

Autoscaling is enabled by default with one minimum task and three maximum tasks. For production, start with at least two minimum tasks when availability matters. When autoscaling is disabled, Desired tasks controls how many web tasks the ECS service keeps running.
Autoscaling is enabled by default with one minimum task and three maximum tasks. High-resolution scaling metrics are also enabled by default, publishing the built-in CPU and configured memory metrics every 20 seconds so target tracking can react faster; this adds CloudWatch charges. Turn off High-resolution scaling metrics to use the standard 60-second policies. For production, start with at least two minimum tasks when availability matters. When autoscaling is disabled, Desired tasks controls how many web tasks the ECS service keeps running.

| Field | Default | Description |
| ------------------------ | ------- | ---------------------------------------------- |
| Autoscaling | true | Enables ECS service target tracking |
| Minimum tasks | 1 | Lower bound for running tasks |
| Maximum tasks | 3 | Upper bound for running tasks |
| Desired tasks | 1 | Running tasks when autoscaling is disabled |
| High-resolution scaling metrics | true | Uses 20-second CPU and memory scaling metrics |
| CPU target (%) | 70 | Average CPU utilization target |
| Memory target (%) | 80 | Average memory utilization target |
| Scale-in cooldown (secs) | 300 | Delay before another scale-in action |
Expand Down Expand Up @@ -813,6 +814,7 @@ module:
| Post-deploy ephemeral storage | No | Task definition default | Ephemeral storage override for the post-deploy task |
| Post-deploy timeout (secs) | No | 1800 | Maximum post-deploy task wait time |
| Autoscaling | No | true | Enable CPU and optional memory target tracking |
| High-resolution scaling metrics | No | true | Use 20-second CPU and memory scaling metrics |
| Desired tasks | Yes* | 1 | Number of web tasks when autoscaling is disabled |
| Deployment strategy | Yes | rolling | Rolling, Blue/green, Linear, or Canary |
| EFS file system | No | false | Mount an EFS file system into the app container |
Expand Down
7 changes: 4 additions & 3 deletions compute/ecs_service/rvn-ecs-worker-definition.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ definition:
name: ECS Worker
description: Background worker ECS service for running private container workloads without exposed ports.
release:
version: 0.3.2
description: "Accept mixed IAM policy document shapes for task role inline policies."
version: 1.0.0
description: "Enable high-resolution ECS autoscaling metrics by default."
module:
inputs:
- id: section_cluster
Expand Down Expand Up @@ -181,7 +181,7 @@ module:

## Autoscaling

Autoscaling is enabled by default with CPU and memory target tracking. Use custom metric scaling policies for queue depth, lag, backlog, or other workload-specific metrics that better represent worker demand. When autoscaling is disabled, Desired tasks controls how many worker tasks the ECS service keeps running.
Autoscaling is enabled by default with CPU and memory target tracking. High-resolution scaling metrics are also enabled by default, publishing both built-in metrics every 20 seconds so target tracking can react faster; this adds CloudWatch charges. Turn off High-resolution scaling metrics to use the standard 60-second policies. Use custom metric scaling policies for queue depth, lag, backlog, or other workload-specific metrics that better represent worker demand. When autoscaling is disabled, Desired tasks controls how many worker tasks the ECS service keeps running.

## Configuration

Expand All @@ -196,6 +196,7 @@ module:
| App size | No | 2 vCPU, 4 GB | Fargate task CPU and memory |
| App ephemeral storage (GiB) | No | 20 | Fargate task ephemeral storage |
| Autoscaling | No | true | Enable CPU and optional memory target tracking |
| High-resolution scaling metrics | No | true | Use 20-second CPU and memory scaling metrics |
| Desired tasks | Yes* | 1 | Number of worker tasks when autoscaling is disabled |
| Custom metric scaling policies | No | [] | Queue/backlog-oriented target tracking policies |
| Runtime environment variables | No | - | Environment variables passed to the worker container |
Expand Down
127 changes: 127 additions & 0 deletions compute/ecs_service/tests/basic.tftest.hcl
Original file line number Diff line number Diff line change
Expand Up @@ -515,6 +515,133 @@ run "service_with_auto_scaling" {
condition = aws_appautoscaling_target.this[0].max_capacity == 10
error_message = "Auto scaling max capacity should be 10"
}

assert {
condition = length(aws_ecs_service.this.monitoring) == 0
error_message = "Standard-resolution policies should omit ECS service monitoring"
}
}

run "service_with_high_resolution_cpu_auto_scaling" {
command = plan

variables {
steady_state_wait_enabled = false
auto_scaling = {
min_capacity = 1
max_capacity = 10
target_tracking = [{
policy_name = "cpu-scaling"
target_value = 70
predefined_metric = "ECSServiceAverageCPUUtilizationHighResolution"
}]
}
}

assert {
condition = aws_ecs_service.this.wait_for_steady_state
error_message = "High-resolution monitoring should force steady-state waiting"
}

assert {
condition = length(aws_ecs_service.this.monitoring) == 1
error_message = "High-resolution CPU scaling should enable ECS service monitoring"
}

assert {
condition = aws_ecs_service.this.monitoring[0].metric_configuration[0].resolution_seconds == 20
error_message = "High-resolution ECS service monitoring should use 20-second metrics"
}

assert {
condition = toset(aws_ecs_service.this.monitoring[0].metric_configuration[0].metric_names) == toset(["CPUUtilization"])
error_message = "High-resolution CPU scaling should publish CPUUtilization"
}
}

run "service_with_high_resolution_cpu_and_memory_auto_scaling" {
command = plan

variables {
auto_scaling = {
min_capacity = 1
max_capacity = 10
target_tracking = [
{
policy_name = "cpu-scaling"
target_value = 70
predefined_metric = "ECSServiceAverageCPUUtilizationHighResolution"
},
{
policy_name = "memory-scaling"
target_value = 80
predefined_metric = "ECSServiceAverageMemoryUtilizationHighResolution"
}
]
}
}

assert {
condition = toset(aws_ecs_service.this.monitoring[0].metric_configuration[0].metric_names) == toset([
"CPUUtilization",
"MemoryUtilization",
])
error_message = "High-resolution CPU and memory scaling should publish both ECS metrics"
}
}

run "service_with_custom_metric_auto_scaling" {
command = plan

variables {
steady_state_wait_enabled = false
auto_scaling = {
min_capacity = 1
max_capacity = 10
target_tracking = [{
policy_name = "custom-scaling"
target_value = 100
custom_metric = {
metric_name = "WorkItemsPerTask"
namespace = "Custom/ECSScaling"
statistic = "Average"
}
}]
}
}

assert {
condition = length(aws_ecs_service.this.monitoring) == 0 && !aws_ecs_service.this.wait_for_steady_state
error_message = "Custom metrics should not enable ECS monitoring or force steady-state waiting"
}
}

run "disabled_auto_scaling_ignores_high_resolution_policies" {
command = plan

variables {
steady_state_wait_enabled = false
auto_scaling = {
enabled = false
min_capacity = 1
max_capacity = 10
target_tracking = [{
policy_name = "cpu-scaling"
target_value = 70
predefined_metric = "ECSServiceAverageCPUUtilizationHighResolution"
}]
}
}

assert {
condition = length(aws_appautoscaling_target.this) == 0
error_message = "Disabled auto scaling should omit the scalable target"
}

assert {
condition = length(aws_ecs_service.this.monitoring) == 0 && !aws_ecs_service.this.wait_for_steady_state
error_message = "Disabled auto scaling should omit ECS monitoring and not force steady-state waiting"
}
}

################################################################################
Expand Down
2 changes: 1 addition & 1 deletion compute/ecs_service/variables.tf
Original file line number Diff line number Diff line change
Expand Up @@ -664,7 +664,7 @@ variable "auto_scaling" {
end_time = optional(string, null)
})), [])
})
description = "Auto scaling configuration for the service."
description = "Auto scaling configuration for the service. ECSServiceAverageCPUUtilizationHighResolution and ECSServiceAverageMemoryUtilizationHighResolution automatically enable 20-second ECS service monitoring for their corresponding built-in metrics."
default = null
}

Expand Down
3 changes: 2 additions & 1 deletion compute/ecs_service/versions.tf
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,10 @@ terraform {
source = "hashicorp/aws"
# 6.21 adds linear_configuration / canary_configuration on the
# aws_ecs_service deployment_configuration block.
# TODO(ENG-4941): Raise this minimum to the first release containing
# https://github.com/hashicorp/terraform-provider-aws/pull/48792.
version = ">= 6.21"
}
}
}


Loading
Loading