diff --git a/README.md b/README.md index 5e1ba9e..394d4c1 100644 --- a/README.md +++ b/README.md @@ -155,7 +155,7 @@ module "ecs" { | Name | Version | |------|---------| | [terraform](#requirement\_terraform) | >= 1.5.7 | -| [aws](#requirement\_aws) | >= 6.0 | +| [aws](#requirement\_aws) | >= 6.4 | ## Providers @@ -193,7 +193,7 @@ No resources. | [create\_task\_exec\_policy](#input\_create\_task\_exec\_policy) | Determines whether the ECS task definition IAM policy should be created. This includes permissions included in AmazonECSTaskExecutionRolePolicy as well as access to secrets and SSM parameters | `bool` | `true` | no | | [default\_capacity\_provider\_strategy](#input\_default\_capacity\_provider\_strategy) | Map of default capacity provider strategy definitions to use for the cluster |
map(object({
base = optional(number)
name = optional(string) # Will fall back to use map key if not set
weight = optional(number)
})) | `null` | no |
| [region](#input\_region) | Region where the resource(s) will be managed. Defaults to the Region set in the provider configuration | `string` | `null` | no |
-| [services](#input\_services) | Map of service definitions to create | map(object({
create = optional(bool, true)
create_service = optional(bool, true)
tags = optional(map(string), {})
# Service
ignore_task_definition_changes = optional(bool, false)
alarms = optional(object({
alarm_names = list(string)
enable = optional(bool, true)
rollback = optional(bool, true)
}))
availability_zone_rebalancing = optional(string)
capacity_provider_strategy = optional(map(object({
base = optional(number)
capacity_provider = string
weight = optional(number)
})))
deployment_circuit_breaker = optional(object({
enable = bool
rollback = bool
}))
deployment_controller = optional(object({
type = optional(string)
}))
deployment_maximum_percent = optional(number, 200)
deployment_minimum_healthy_percent = optional(number, 66)
desired_count = optional(number, 1)
enable_ecs_managed_tags = optional(bool, true)
enable_execute_command = optional(bool, false)
force_delete = optional(bool)
force_new_deployment = optional(bool, true)
health_check_grace_period_seconds = optional(number)
launch_type = optional(string, "FARGATE")
load_balancer = optional(map(object({
container_name = string
container_port = number
elb_name = optional(string)
target_group_arn = optional(string)
})))
name = optional(string) # Will fall back to use map key if not set
assign_public_ip = optional(bool, false)
security_group_ids = optional(list(string), [])
subnet_ids = optional(list(string), [])
ordered_placement_strategy = optional(map(object({
field = optional(string)
type = string
})))
placement_constraints = optional(map(object({
expression = optional(string)
type = string
})))
platform_version = optional(string)
propagate_tags = optional(string)
scheduling_strategy = optional(string)
service_connect_configuration = optional(object({
enabled = optional(bool, true)
log_configuration = optional(object({
log_driver = string
options = optional(map(string))
secret_option = optional(list(object({
name = string
value_from = string
})))
}))
namespace = optional(string)
service = optional(list(object({
client_alias = optional(object({
dns_name = optional(string)
port = number
}))
discovery_name = optional(string)
ingress_port_override = optional(number)
port_name = string
timeout = optional(object({
idle_timeout_seconds = optional(number)
per_request_timeout_seconds = optional(number)
}))
tls = optional(object({
issuer_cert_authority = object({
aws_pca_authority_arn = string
})
kms_key = optional(string)
role_arn = optional(string)
}))
})))
}))
service_registries = optional(object({
container_name = optional(string)
container_port = optional(number)
port = optional(number)
registry_arn = string
}))
timeouts = optional(object({
create = optional(string)
update = optional(string)
delete = optional(string)
}))
triggers = optional(map(string))
volume_configuration = optional(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)
})
}))
vpc_lattice_configurations = optional(object({
role_arn = string
target_group_arn = string
port_name = string
}))
wait_for_steady_state = optional(bool)
service_tags = optional(map(string), {})
# Service - IAM Role
create_iam_role = optional(bool, true)
iam_role_arn = optional(string)
iam_role_name = optional(string)
iam_role_use_name_prefix = optional(bool, true)
iam_role_path = optional(string)
iam_role_description = optional(string)
iam_role_permissions_boundary = optional(string)
iam_role_tags = optional(map(string), {})
iam_role_statements = optional(list(object({
sid = optional(string)
actions = optional(list(string))
not_actions = optional(list(string))
effect = optional(string)
resources = optional(list(string))
not_resources = optional(list(string))
principals = optional(list(object({
type = string
identifiers = list(string)
})))
not_principals = optional(list(object({
type = string
identifiers = list(string)
})))
condition = optional(list(object({
test = string
values = list(string)
variable = string
})))
})))
# Task Definition
create_task_definition = optional(bool, true)
task_definition_arn = optional(string)
container_definitions = optional(map(object({
operating_system_family = optional(string)
tags = optional(map(string))
# Container definition
command = optional(list(string))
cpu = optional(number)
dependsOn = optional(list(object({
condition = string
containerName = string
})))
disableNetworking = optional(bool)
dnsSearchDomains = optional(list(string))
dnsServers = optional(list(string))
dockerLabels = optional(map(string))
dockerSecurityOptions = optional(list(string))
enable_execute_command = optional(bool)
entrypoint = optional(list(string))
environment = optional(list(object({
name = string
value = string
})))
environmentFiles = optional(list(object({
type = string
value = string
})))
essential = optional(bool)
extraHosts = optional(list(object({
hostname = string
ipAddress = string
})))
firelensConfiguration = optional(object({
options = optional(map(string))
type = optional(string)
}))
healthCheck = optional(object({
command = optional(list(string))
interval = optional(number)
retries = optional(number)
startPeriod = optional(number)
timeout = optional(number)
}))
hostname = optional(string)
image = optional(string)
interactive = optional(bool)
links = optional(list(string))
linuxParameters = optional(object({
capabilities = optional(object({
add = optional(list(string))
drop = optional(list(string))
}))
devices = optional(list(object({
containerPath = optional(string)
hostPath = optional(string)
permissions = optional(list(string))
})))
initProcessEnabled = optional(bool)
maxSwap = optional(number)
sharedMemorySize = optional(number)
swappiness = optional(number)
tmpfs = optional(list(object({
containerPath = string
mountOptions = optional(list(string))
size = number
})))
}))
logConfiguration = optional(object({
logDriver = optional(string)
options = optional(map(string))
secretOptions = optional(list(object({
name = string
valueFrom = string
})))
}))
memory = optional(number)
memoryReservation = optional(number)
mountPoints = optional(list(object({
containerPath = optional(string)
readOnly = optional(bool)
sourceVolume = optional(string)
})), [])
name = optional(string)
portMappings = optional(list(object({
appProtocol = optional(string)
containerPort = optional(number)
containerPortRange = optional(string)
hostPort = optional(number)
name = optional(string)
protocol = optional(string)
})), [])
privileged = optional(bool)
pseudoTerminal = optional(bool)
readonlyRootFilesystem = optional(bool)
repositoryCredentials = optional(object({
credentialsParameter = optional(string)
}))
resourceRequirements = optional(list(object({
type = string
value = string
})))
restartPolicy = optional(object({
enabled = optional(bool)
ignoredExitCodes = optional(list(number))
restartAttemptPeriod = optional(number)
}))
secrets = optional(list(object({
name = string
valueFrom = string
})))
startTimeout = optional(number)
stopTimeout = optional(number)
systemControls = optional(list(object({
namespace = optional(string)
value = optional(string)
})))
ulimits = optional(list(object({
hardLimit = number
name = string
softLimit = number
})))
user = optional(string)
versionConsistency = optional(string)
volumesFrom = optional(list(object({
readOnly = optional(bool)
sourceContainer = optional(string)
})))
workingDirectory = optional(string)
# Cloudwatch Log Group
service = optional(string, "")
enable_cloudwatch_logging = optional(bool, true)
create_cloudwatch_log_group = optional(bool, true)
cloudwatch_log_group_name = optional(string)
cloudwatch_log_group_use_name_prefix = optional(bool, false)
cloudwatch_log_group_class = optional(string)
cloudwatch_log_group_retention_in_days = optional(number)
cloudwatch_log_group_kms_key_id = optional(string)
})))
cpu = optional(number, 1024)
enable_fault_injection = optional(bool)
ephemeral_storage = optional(object({
size_in_gib = number
}))
family = optional(string)
ipc_mode = optional(string)
memory = optional(number, 2048)
network_mode = optional(string, "awsvpc")
pid_mode = optional(string)
proxy_configuration = optional(object({
container_name = string
properties = optional(map(string))
type = optional(string)
}))
requires_compatibilities = optional(list(string), ["FARGATE"])
runtime_platform = optional(object({
cpu_architecture = optional(string, "X86_64")
operating_system_family = optional(string, "LINUX")
}),
# Default
{
cpu_architecture = "X86_64"
operating_system_family = "LINUX"
}
)
skip_destroy = optional(bool)
task_definition_placement_constraints = optional(map(object({
expression = optional(string)
type = string
})))
track_latest = optional(bool, true)
volume = optional(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)
})))
task_tags = optional(map(string), {})
# Task Execution - IAM Role
create_task_exec_iam_role = optional(bool, true)
task_exec_iam_role_arn = optional(string)
task_exec_iam_role_name = optional(string)
task_exec_iam_role_use_name_prefix = optional(bool, true)
task_exec_iam_role_path = optional(string)
task_exec_iam_role_description = optional(string)
task_exec_iam_role_permissions_boundary = optional(string)
task_exec_iam_role_tags = optional(map(string), {})
task_exec_iam_role_policies = optional(map(string), {})
task_exec_iam_role_max_session_duration = optional(number)
create_task_exec_policy = optional(bool, true)
task_exec_ssm_param_arns = optional(list(string), [])
task_exec_secret_arns = optional(list(string), [])
task_exec_iam_statements = optional(list(object({
sid = optional(string)
actions = optional(list(string))
not_actions = optional(list(string))
effect = optional(string)
resources = optional(list(string))
not_resources = optional(list(string))
principals = optional(list(object({
type = string
identifiers = list(string)
})))
not_principals = optional(list(object({
type = string
identifiers = list(string)
})))
condition = optional(list(object({
test = string
values = list(string)
variable = string
})))
})))
task_exec_iam_policy_path = optional(string)
# Tasks - IAM Role
create_tasks_iam_role = optional(bool, true)
tasks_iam_role_arn = optional(string)
tasks_iam_role_name = optional(string)
tasks_iam_role_use_name_prefix = optional(bool, true)
tasks_iam_role_path = optional(string)
tasks_iam_role_description = optional(string)
tasks_iam_role_permissions_boundary = optional(string)
tasks_iam_role_tags = optional(map(string), {})
tasks_iam_role_policies = optional(map(string), {})
tasks_iam_role_statements = optional(list(object({
sid = optional(string)
actions = optional(list(string))
not_actions = optional(list(string))
effect = optional(string)
resources = optional(list(string))
not_resources = optional(list(string))
principals = optional(list(object({
type = string
identifiers = list(string)
})))
not_principals = optional(list(object({
type = string
identifiers = list(string)
})))
condition = optional(list(object({
test = string
values = list(string)
variable = string
})))
})))
# Task Set
external_id = optional(string)
scale = optional(object({
unit = optional(string)
value = optional(number)
}))
wait_until_stable = optional(bool)
wait_until_stable_timeout = optional(string)
# Autoscaling
enable_autoscaling = optional(bool, true)
autoscaling_min_capacity = optional(number, 1)
autoscaling_max_capacity = optional(number, 10)
autoscaling_policies = optional(map(object({
name = optional(string) # Will fall back to the key name if not provided
policy_type = optional(string, "TargetTrackingScaling")
step_scaling_policy_configuration = optional(object({
adjustment_type = optional(string)
cooldown = optional(number)
metric_aggregation_type = optional(string)
min_adjustment_magnitude = optional(number)
step_adjustment = optional(list(object({
metric_interval_lower_bound = optional(string)
metric_interval_upper_bound = optional(string)
scaling_adjustment = number
})))
}))
target_tracking_scaling_policy_configuration = optional(object({
customized_metric_specification = optional(object({
dimensions = optional(list(object({
name = string
value = string
})))
metric_name = optional(string)
metrics = optional(list(object({
expression = optional(string)
id = string
label = optional(string)
metric_stat = optional(object({
metric = object({
dimensions = optional(list(object({
name = string
value = string
})))
metric_name = string
namespace = string
})
stat = string
unit = optional(string)
}))
return_data = optional(bool)
})))
namespace = optional(string)
statistic = optional(string)
unit = optional(string)
}))
disable_scale_in = optional(bool)
predefined_metric_specification = optional(object({
predefined_metric_type = string
resource_label = optional(string)
}))
scale_in_cooldown = optional(number, 300)
scale_out_cooldown = optional(number, 60)
target_value = optional(number, 75)
}))
})),
# Default
{
cpu = {
policy_type = "TargetTrackingScaling"
target_tracking_scaling_policy_configuration = {
predefined_metric_specification = {
predefined_metric_type = "ECSServiceAverageCPUUtilization"
}
}
}
memory = {
policy_type = "TargetTrackingScaling"
target_tracking_scaling_policy_configuration = {
predefined_metric_specification = {
predefined_metric_type = "ECSServiceAverageMemoryUtilization"
}
}
}
}
)
autoscaling_scheduled_actions = optional(map(object({
name = optional(string)
min_capacity = number
max_capacity = number
schedule = string
start_time = optional(string)
end_time = optional(string)
timezone = optional(string)
})))
# Security Group
create_security_group = optional(bool, true)
security_group_name = optional(string)
security_group_use_name_prefix = optional(bool, true)
security_group_description = optional(string)
security_group_ingress_rules = optional(map(object({
cidr_ipv4 = optional(string)
cidr_ipv6 = optional(string)
description = optional(string)
from_port = optional(string)
ip_protocol = optional(string, "tcp")
prefix_list_id = optional(string)
referenced_security_group_id = optional(string)
tags = optional(map(string), {})
to_port = optional(string)
})),
# Default
{}
)
security_group_egress_rules = optional(map(object({
cidr_ipv4 = optional(string)
cidr_ipv6 = optional(string)
description = optional(string)
from_port = optional(string)
ip_protocol = optional(string, "tcp")
prefix_list_id = optional(string)
referenced_security_group_id = optional(string)
tags = optional(map(string), {})
to_port = optional(string)
})),
# Default
{}
)
security_group_tags = optional(map(string), {})
# ECS Infrastructure IAM Role
create_infrastructure_iam_role = optional(bool, true)
infrastructure_iam_role_arn = optional(string)
infrastructure_iam_role_name = optional(string)
infrastructure_iam_role_use_name_prefix = optional(bool, true)
infrastructure_iam_role_path = optional(string)
infrastructure_iam_role_description = optional(string)
infrastructure_iam_role_permissions_boundary = optional(string)
infrastructure_iam_role_tags = optional(map(string), {})
})) | `null` | no |
+| [services](#input\_services) | Map of service definitions to create | map(object({
create = optional(bool, true)
create_service = optional(bool, true)
tags = optional(map(string), {})
# Service
ignore_task_definition_changes = optional(bool, false)
alarms = optional(object({
alarm_names = list(string)
enable = optional(bool, true)
rollback = optional(bool, true)
}))
availability_zone_rebalancing = optional(string)
capacity_provider_strategy = optional(map(object({
base = optional(number)
capacity_provider = string
weight = optional(number)
})))
deployment_circuit_breaker = optional(object({
enable = bool
rollback = bool
}))
deployment_configuration = optional(object({
strategy = optional(string)
bake_time_in_minutes = optional(string)
lifecycle_hook = optional(map(object({
hook_target_arn = string
role_arn = string
lifecycle_stages = list(string)
})))
}))
deployment_controller = optional(object({
type = optional(string)
}))
deployment_maximum_percent = optional(number, 200)
deployment_minimum_healthy_percent = optional(number, 66)
desired_count = optional(number, 1)
enable_ecs_managed_tags = optional(bool, true)
enable_execute_command = optional(bool, false)
force_delete = optional(bool)
force_new_deployment = optional(bool, true)
health_check_grace_period_seconds = optional(number)
launch_type = optional(string, "FARGATE")
load_balancer = optional(map(object({
container_name = string
container_port = number
elb_name = optional(string)
target_group_arn = optional(string)
advanced_configuration = optional(object({
alternate_target_group_arn = string
production_listener_rule = string
role_arn = string
test_listener_rule = optional(string)
}))
})))
name = optional(string) # Will fall back to use map key if not set
assign_public_ip = optional(bool, false)
security_group_ids = optional(list(string), [])
subnet_ids = optional(list(string), [])
ordered_placement_strategy = optional(map(object({
field = optional(string)
type = string
})))
placement_constraints = optional(map(object({
expression = optional(string)
type = string
})))
platform_version = optional(string)
propagate_tags = optional(string)
scheduling_strategy = optional(string)
service_connect_configuration = optional(object({
enabled = optional(bool, true)
log_configuration = optional(object({
log_driver = string
options = optional(map(string))
secret_option = optional(list(object({
name = string
value_from = string
})))
}))
namespace = optional(string)
service = optional(list(object({
client_alias = optional(object({
dns_name = optional(string)
port = number
test_traffic_rules = optional(list(object({
header = optional(object({
name = string
value = object({
exact = string
})
}))
})))
}))
discovery_name = optional(string)
ingress_port_override = optional(number)
port_name = string
timeout = optional(object({
idle_timeout_seconds = optional(number)
per_request_timeout_seconds = optional(number)
}))
tls = optional(object({
issuer_cert_authority = object({
aws_pca_authority_arn = string
})
kms_key = optional(string)
role_arn = optional(string)
}))
})))
}))
service_registries = optional(object({
container_name = optional(string)
container_port = optional(number)
port = optional(number)
registry_arn = string
}))
timeouts = optional(object({
create = optional(string)
update = optional(string)
delete = optional(string)
}))
triggers = optional(map(string))
volume_configuration = optional(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)
})
}))
vpc_lattice_configurations = optional(object({
role_arn = string
target_group_arn = string
port_name = string
}))
wait_for_steady_state = optional(bool)
service_tags = optional(map(string), {})
# Service - IAM Role
create_iam_role = optional(bool, true)
iam_role_arn = optional(string)
iam_role_name = optional(string)
iam_role_use_name_prefix = optional(bool, true)
iam_role_path = optional(string)
iam_role_description = optional(string)
iam_role_permissions_boundary = optional(string)
iam_role_tags = optional(map(string), {})
iam_role_statements = optional(list(object({
sid = optional(string)
actions = optional(list(string))
not_actions = optional(list(string))
effect = optional(string)
resources = optional(list(string))
not_resources = optional(list(string))
principals = optional(list(object({
type = string
identifiers = list(string)
})))
not_principals = optional(list(object({
type = string
identifiers = list(string)
})))
condition = optional(list(object({
test = string
values = list(string)
variable = string
})))
})))
# Task Definition
create_task_definition = optional(bool, true)
task_definition_arn = optional(string)
container_definitions = optional(map(object({
operating_system_family = optional(string)
tags = optional(map(string))
# Container definition
command = optional(list(string))
cpu = optional(number)
dependsOn = optional(list(object({
condition = string
containerName = string
})))
disableNetworking = optional(bool)
dnsSearchDomains = optional(list(string))
dnsServers = optional(list(string))
dockerLabels = optional(map(string))
dockerSecurityOptions = optional(list(string))
enable_execute_command = optional(bool)
entrypoint = optional(list(string))
environment = optional(list(object({
name = string
value = string
})))
environmentFiles = optional(list(object({
type = string
value = string
})))
essential = optional(bool)
extraHosts = optional(list(object({
hostname = string
ipAddress = string
})))
firelensConfiguration = optional(object({
options = optional(map(string))
type = optional(string)
}))
healthCheck = optional(object({
command = optional(list(string))
interval = optional(number)
retries = optional(number)
startPeriod = optional(number)
timeout = optional(number)
}))
hostname = optional(string)
image = optional(string)
interactive = optional(bool)
links = optional(list(string))
linuxParameters = optional(object({
capabilities = optional(object({
add = optional(list(string))
drop = optional(list(string))
}))
devices = optional(list(object({
containerPath = optional(string)
hostPath = optional(string)
permissions = optional(list(string))
})))
initProcessEnabled = optional(bool)
maxSwap = optional(number)
sharedMemorySize = optional(number)
swappiness = optional(number)
tmpfs = optional(list(object({
containerPath = string
mountOptions = optional(list(string))
size = number
})))
}))
logConfiguration = optional(object({
logDriver = optional(string)
options = optional(map(string))
secretOptions = optional(list(object({
name = string
valueFrom = string
})))
}))
memory = optional(number)
memoryReservation = optional(number)
mountPoints = optional(list(object({
containerPath = optional(string)
readOnly = optional(bool)
sourceVolume = optional(string)
})), [])
name = optional(string)
portMappings = optional(list(object({
appProtocol = optional(string)
containerPort = optional(number)
containerPortRange = optional(string)
hostPort = optional(number)
name = optional(string)
protocol = optional(string)
})), [])
privileged = optional(bool)
pseudoTerminal = optional(bool)
readonlyRootFilesystem = optional(bool)
repositoryCredentials = optional(object({
credentialsParameter = optional(string)
}))
resourceRequirements = optional(list(object({
type = string
value = string
})))
restartPolicy = optional(object({
enabled = optional(bool)
ignoredExitCodes = optional(list(number))
restartAttemptPeriod = optional(number)
}))
secrets = optional(list(object({
name = string
valueFrom = string
})))
startTimeout = optional(number)
stopTimeout = optional(number)
systemControls = optional(list(object({
namespace = optional(string)
value = optional(string)
})))
ulimits = optional(list(object({
hardLimit = number
name = string
softLimit = number
})))
user = optional(string)
versionConsistency = optional(string)
volumesFrom = optional(list(object({
readOnly = optional(bool)
sourceContainer = optional(string)
})))
workingDirectory = optional(string)
# Cloudwatch Log Group
service = optional(string, "")
enable_cloudwatch_logging = optional(bool, true)
create_cloudwatch_log_group = optional(bool, true)
cloudwatch_log_group_name = optional(string)
cloudwatch_log_group_use_name_prefix = optional(bool, false)
cloudwatch_log_group_class = optional(string)
cloudwatch_log_group_retention_in_days = optional(number)
cloudwatch_log_group_kms_key_id = optional(string)
})))
cpu = optional(number, 1024)
enable_fault_injection = optional(bool)
ephemeral_storage = optional(object({
size_in_gib = number
}))
family = optional(string)
ipc_mode = optional(string)
memory = optional(number, 2048)
network_mode = optional(string, "awsvpc")
pid_mode = optional(string)
proxy_configuration = optional(object({
container_name = string
properties = optional(map(string))
type = optional(string)
}))
requires_compatibilities = optional(list(string), ["FARGATE"])
runtime_platform = optional(object({
cpu_architecture = optional(string, "X86_64")
operating_system_family = optional(string, "LINUX")
}),
# Default
{
cpu_architecture = "X86_64"
operating_system_family = "LINUX"
}
)
skip_destroy = optional(bool)
task_definition_placement_constraints = optional(map(object({
expression = optional(string)
type = string
})))
track_latest = optional(bool, true)
volume = optional(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)
})))
task_tags = optional(map(string), {})
# Task Execution - IAM Role
create_task_exec_iam_role = optional(bool, true)
task_exec_iam_role_arn = optional(string)
task_exec_iam_role_name = optional(string)
task_exec_iam_role_use_name_prefix = optional(bool, true)
task_exec_iam_role_path = optional(string)
task_exec_iam_role_description = optional(string)
task_exec_iam_role_permissions_boundary = optional(string)
task_exec_iam_role_tags = optional(map(string), {})
task_exec_iam_role_policies = optional(map(string), {})
task_exec_iam_role_max_session_duration = optional(number)
create_task_exec_policy = optional(bool, true)
task_exec_ssm_param_arns = optional(list(string), [])
task_exec_secret_arns = optional(list(string), [])
task_exec_iam_statements = optional(list(object({
sid = optional(string)
actions = optional(list(string))
not_actions = optional(list(string))
effect = optional(string)
resources = optional(list(string))
not_resources = optional(list(string))
principals = optional(list(object({
type = string
identifiers = list(string)
})))
not_principals = optional(list(object({
type = string
identifiers = list(string)
})))
condition = optional(list(object({
test = string
values = list(string)
variable = string
})))
})))
task_exec_iam_policy_path = optional(string)
# Tasks - IAM Role
create_tasks_iam_role = optional(bool, true)
tasks_iam_role_arn = optional(string)
tasks_iam_role_name = optional(string)
tasks_iam_role_use_name_prefix = optional(bool, true)
tasks_iam_role_path = optional(string)
tasks_iam_role_description = optional(string)
tasks_iam_role_permissions_boundary = optional(string)
tasks_iam_role_tags = optional(map(string), {})
tasks_iam_role_policies = optional(map(string), {})
tasks_iam_role_statements = optional(list(object({
sid = optional(string)
actions = optional(list(string))
not_actions = optional(list(string))
effect = optional(string)
resources = optional(list(string))
not_resources = optional(list(string))
principals = optional(list(object({
type = string
identifiers = list(string)
})))
not_principals = optional(list(object({
type = string
identifiers = list(string)
})))
condition = optional(list(object({
test = string
values = list(string)
variable = string
})))
})))
# Task Set
external_id = optional(string)
scale = optional(object({
unit = optional(string)
value = optional(number)
}))
wait_until_stable = optional(bool)
wait_until_stable_timeout = optional(string)
# Autoscaling
enable_autoscaling = optional(bool, true)
autoscaling_min_capacity = optional(number, 1)
autoscaling_max_capacity = optional(number, 10)
autoscaling_policies = optional(map(object({
name = optional(string) # Will fall back to the key name if not provided
policy_type = optional(string, "TargetTrackingScaling")
step_scaling_policy_configuration = optional(object({
adjustment_type = optional(string)
cooldown = optional(number)
metric_aggregation_type = optional(string)
min_adjustment_magnitude = optional(number)
step_adjustment = optional(list(object({
metric_interval_lower_bound = optional(string)
metric_interval_upper_bound = optional(string)
scaling_adjustment = number
})))
}))
target_tracking_scaling_policy_configuration = optional(object({
customized_metric_specification = optional(object({
dimensions = optional(list(object({
name = string
value = string
})))
metric_name = optional(string)
metrics = optional(list(object({
expression = optional(string)
id = string
label = optional(string)
metric_stat = optional(object({
metric = object({
dimensions = optional(list(object({
name = string
value = string
})))
metric_name = string
namespace = string
})
stat = string
unit = optional(string)
}))
return_data = optional(bool)
})))
namespace = optional(string)
statistic = optional(string)
unit = optional(string)
}))
disable_scale_in = optional(bool)
predefined_metric_specification = optional(object({
predefined_metric_type = string
resource_label = optional(string)
}))
scale_in_cooldown = optional(number, 300)
scale_out_cooldown = optional(number, 60)
target_value = optional(number, 75)
}))
})),
# Default
{
cpu = {
policy_type = "TargetTrackingScaling"
target_tracking_scaling_policy_configuration = {
predefined_metric_specification = {
predefined_metric_type = "ECSServiceAverageCPUUtilization"
}
}
}
memory = {
policy_type = "TargetTrackingScaling"
target_tracking_scaling_policy_configuration = {
predefined_metric_specification = {
predefined_metric_type = "ECSServiceAverageMemoryUtilization"
}
}
}
}
)
autoscaling_scheduled_actions = optional(map(object({
name = optional(string)
min_capacity = number
max_capacity = number
schedule = string
start_time = optional(string)
end_time = optional(string)
timezone = optional(string)
})))
# Security Group
create_security_group = optional(bool, true)
security_group_name = optional(string)
security_group_use_name_prefix = optional(bool, true)
security_group_description = optional(string)
security_group_ingress_rules = optional(map(object({
cidr_ipv4 = optional(string)
cidr_ipv6 = optional(string)
description = optional(string)
from_port = optional(string)
ip_protocol = optional(string, "tcp")
prefix_list_id = optional(string)
referenced_security_group_id = optional(string)
tags = optional(map(string), {})
to_port = optional(string)
})),
# Default
{}
)
security_group_egress_rules = optional(map(object({
cidr_ipv4 = optional(string)
cidr_ipv6 = optional(string)
description = optional(string)
from_port = optional(string)
ip_protocol = optional(string, "tcp")
prefix_list_id = optional(string)
referenced_security_group_id = optional(string)
tags = optional(map(string), {})
to_port = optional(string)
})),
# Default
{}
)
security_group_tags = optional(map(string), {})
# ECS Infrastructure IAM Role
create_infrastructure_iam_role = optional(bool, true)
infrastructure_iam_role_arn = optional(string)
infrastructure_iam_role_name = optional(string)
infrastructure_iam_role_use_name_prefix = optional(bool, true)
infrastructure_iam_role_path = optional(string)
infrastructure_iam_role_description = optional(string)
infrastructure_iam_role_permissions_boundary = optional(string)
infrastructure_iam_role_tags = optional(map(string), {})
})) | `null` | no |
| [tags](#input\_tags) | A map of tags to add to all resources | `map(string)` | `{}` | no |
| [task\_exec\_iam\_role\_description](#input\_task\_exec\_iam\_role\_description) | Description of the role | `string` | `null` | no |
| [task\_exec\_iam\_role\_name](#input\_task\_exec\_iam\_role\_name) | Name to use on IAM role created | `string` | `null` | no |
diff --git a/examples/complete/README.md b/examples/complete/README.md
index 090e1c3..817eb4e 100644
--- a/examples/complete/README.md
+++ b/examples/complete/README.md
@@ -27,13 +27,13 @@ Note that this example may create resources which will incur monetary charges on
| Name | Version |
|------|---------|
| [terraform](#requirement\_terraform) | >= 1.5.7 |
-| [aws](#requirement\_aws) | >= 6.0 |
+| [aws](#requirement\_aws) | >= 6.4 |
## Providers
| Name | Version |
|------|---------|
-| [aws](#provider\_aws) | >= 6.0 |
+| [aws](#provider\_aws) | >= 6.4 |
## Modules
diff --git a/examples/complete/versions.tf b/examples/complete/versions.tf
index db13b0a..497e3e6 100644
--- a/examples/complete/versions.tf
+++ b/examples/complete/versions.tf
@@ -4,7 +4,7 @@ terraform {
required_providers {
aws = {
source = "hashicorp/aws"
- version = ">= 6.0"
+ version = ">= 6.4"
}
}
}
diff --git a/examples/container-definition/README.md b/examples/container-definition/README.md
index 46b7bd3..2a5ab63 100644
--- a/examples/container-definition/README.md
+++ b/examples/container-definition/README.md
@@ -22,7 +22,7 @@ Note that this example may create resources which will incur monetary charges on
| Name | Version |
|------|---------|
| [terraform](#requirement\_terraform) | >= 1.5.7 |
-| [aws](#requirement\_aws) | >= 6.0 |
+| [aws](#requirement\_aws) | >= 6.4 |
| [local](#requirement\_local) | >= 2.5 |
## Providers
diff --git a/examples/container-definition/versions.tf b/examples/container-definition/versions.tf
index f5b245b..9efd186 100644
--- a/examples/container-definition/versions.tf
+++ b/examples/container-definition/versions.tf
@@ -4,7 +4,7 @@ terraform {
required_providers {
aws = {
source = "hashicorp/aws"
- version = ">= 6.0"
+ version = ">= 6.4"
}
local = {
source = "hashicorp/local"
diff --git a/examples/ec2-autoscaling/README.md b/examples/ec2-autoscaling/README.md
index c08d6c1..fd50be0 100644
--- a/examples/ec2-autoscaling/README.md
+++ b/examples/ec2-autoscaling/README.md
@@ -27,13 +27,13 @@ Note that this example may create resources which will incur monetary charges on
| Name | Version |
|------|---------|
| [terraform](#requirement\_terraform) | >= 1.5.7 |
-| [aws](#requirement\_aws) | >= 6.0 |
+| [aws](#requirement\_aws) | >= 6.4 |
## Providers
| Name | Version |
|------|---------|
-| [aws](#provider\_aws) | >= 6.0 |
+| [aws](#provider\_aws) | >= 6.4 |
## Modules
diff --git a/examples/ec2-autoscaling/versions.tf b/examples/ec2-autoscaling/versions.tf
index db13b0a..497e3e6 100644
--- a/examples/ec2-autoscaling/versions.tf
+++ b/examples/ec2-autoscaling/versions.tf
@@ -4,7 +4,7 @@ terraform {
required_providers {
aws = {
source = "hashicorp/aws"
- version = ">= 6.0"
+ version = ">= 6.4"
}
}
}
diff --git a/examples/fargate/README.md b/examples/fargate/README.md
index 440a45c..2a74d68 100644
--- a/examples/fargate/README.md
+++ b/examples/fargate/README.md
@@ -27,13 +27,13 @@ Note that this example may create resources which will incur monetary charges on
| Name | Version |
|------|---------|
| [terraform](#requirement\_terraform) | >= 1.5.7 |
-| [aws](#requirement\_aws) | >= 6.0 |
+| [aws](#requirement\_aws) | >= 6.4 |
## Providers
| Name | Version |
|------|---------|
-| [aws](#provider\_aws) | >= 6.0 |
+| [aws](#provider\_aws) | >= 6.4 |
## Modules
@@ -49,6 +49,9 @@ Note that this example may create resources which will incur monetary charges on
| Name | Type |
|------|------|
+| [aws_iam_role.ecs_elb_permissions](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role) | resource |
+| [aws_iam_role_policy_attachment.ecs_elb_management_role](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource |
+| [aws_iam_role_policy_attachment.ecs_service_role](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource |
| [aws_service_discovery_http_namespace.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/service_discovery_http_namespace) | resource |
| [aws_availability_zones.available](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/availability_zones) | data source |
| [aws_ssm_parameter.fluentbit](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/ssm_parameter) | data source |
diff --git a/examples/fargate/main.tf b/examples/fargate/main.tf
index b84c738..f52e2c6 100644
--- a/examples/fargate/main.tf
+++ b/examples/fargate/main.tf
@@ -60,6 +60,26 @@ module "ecs_service" {
# Enables ECS Exec
enable_execute_command = true
+ # for blue/green deployments
+ deployment_configuration = {
+ strategy = "BLUE_GREEN"
+ bake_time_in_minutes = 2
+
+ # example config using lifecycle hooks
+ # lifecycle_hook = {
+ # success = {
+ # hook_target_arn = aws_lambda_function.hook_success.arn
+ # role_arn = aws_iam_role.global.arn
+ # lifecycle_stages = ["POST_SCALE_UP", "POST_TEST_TRAFFIC_SHIFT"]
+ # }
+ # failure = {
+ # hook_target_arn = aws_lambda_function.hook_failure.arn
+ # role_arn = aws_iam_role.global.arn
+ # lifecycle_stages = ["TEST_TRAFFIC_SHIFT", "POST_PRODUCTION_TRAFFIC_SHIFT"]
+ # }
+ # }
+ }
+
# Container definition(s)
container_definitions = {
@@ -152,6 +172,14 @@ module "ecs_service" {
target_group_arn = module.alb.target_groups["ex_ecs"].arn
container_name = local.container_name
container_port = local.container_port
+
+ # for blue/green deployments
+ advanced_configuration = {
+ alternate_target_group_arn = module.alb.target_groups["ex_ecs_alternate"].arn
+ production_listener_rule = module.alb.listener_rules["ex_http/production"].arn
+ test_listener_rule = module.alb.listener_rules["ex_http/test"].arn
+ role_arn = aws_iam_role.ecs_elb_permissions.arn
+ }
}
}
@@ -176,6 +204,12 @@ module "ecs_service" {
}
tags = local.tags
+
+ depends_on = [
+ aws_iam_role.ecs_elb_permissions,
+ aws_iam_role_policy_attachment.ecs_service_role,
+ aws_iam_role_policy_attachment.ecs_elb_management_role
+ ]
}
################################################################################
@@ -278,8 +312,60 @@ module "alb" {
port = 80
protocol = "HTTP"
- forward = {
- target_group_key = "ex_ecs"
+ fixed_response = {
+ content_type = "text/plain"
+ message_body = "404: Page not found"
+ status_code = "404"
+ }
+
+ # for blue/green deployments
+ rules = {
+ production = {
+ priority = 1
+ actions = [
+ {
+ type = "weighted-forward"
+ target_groups = [
+ {
+ target_group_key = "ex_ecs"
+ weight = 100
+ },
+ {
+ target_group_key = "ex_ecs_alternate"
+ weight = 0
+ }
+ ]
+ }
+ ]
+ conditions = [
+ {
+ path_pattern = {
+ values = ["/*"]
+ }
+ }
+ ]
+ }
+ test = {
+ priority = 2
+ actions = [
+ {
+ type = "weighted-forward"
+ target_groups = [
+ {
+ target_group_key = "ex_ecs_alternate"
+ weight = 100
+ }
+ ]
+ }
+ ]
+ conditions = [
+ {
+ path_pattern = {
+ values = ["/*"]
+ }
+ }
+ ]
+ }
}
}
}
@@ -308,6 +394,31 @@ module "alb" {
# ECS will attach the IPs of the tasks to this target group
create_attachment = false
}
+
+ # for blue/green deployments
+ ex_ecs_alternate = {
+ backend_protocol = "HTTP"
+ backend_port = local.container_port
+ target_type = "ip"
+ deregistration_delay = 5
+ load_balancing_cross_zone_enabled = true
+
+ health_check = {
+ enabled = true
+ healthy_threshold = 5
+ interval = 30
+ matcher = "200"
+ path = "/"
+ port = "traffic-port"
+ protocol = "HTTP"
+ timeout = 5
+ unhealthy_threshold = 2
+ }
+
+ # There's nothing to attach here in this definition. Instead,
+ # ECS will attach the IPs of the tasks to this target group
+ create_attachment = false
+ }
}
tags = local.tags
@@ -329,3 +440,33 @@ module "vpc" {
tags = local.tags
}
+
+resource "aws_iam_role" "ecs_elb_permissions" {
+ name = "${local.name}-ecs-elb-role"
+ assume_role_policy = jsonencode({
+ Version = "2012-10-17"
+ Statement = [
+ {
+ Action = "sts:AssumeRole"
+ Effect = "Allow"
+ Principal = {
+ Service = [
+ "ecs-tasks.amazonaws.com",
+ "ecs.amazonaws.com",
+ ]
+ }
+ }
+ ]
+ })
+}
+
+# for example purposes only
+resource "aws_iam_role_policy_attachment" "ecs_service_role" {
+ role = aws_iam_role.ecs_elb_permissions.name
+ policy_arn = "arn:aws:iam::aws:policy/service-role/AmazonEC2ContainerServiceRole"
+}
+
+resource "aws_iam_role_policy_attachment" "ecs_elb_management_role" {
+ role = aws_iam_role.ecs_elb_permissions.name
+ policy_arn = "arn:aws:iam::aws:policy/AmazonECSInfrastructureRolePolicyForLoadBalancers"
+}
diff --git a/examples/fargate/versions.tf b/examples/fargate/versions.tf
index db13b0a..497e3e6 100644
--- a/examples/fargate/versions.tf
+++ b/examples/fargate/versions.tf
@@ -4,7 +4,7 @@ terraform {
required_providers {
aws = {
source = "hashicorp/aws"
- version = ">= 6.0"
+ version = ">= 6.4"
}
}
}
diff --git a/main.tf b/main.tf
index d2380a5..5e1d1fe 100644
--- a/main.tf
+++ b/main.tf
@@ -65,6 +65,7 @@ module "service" {
capacity_provider_strategy = each.value.capacity_provider_strategy
cluster_arn = module.cluster.arn
deployment_circuit_breaker = each.value.deployment_circuit_breaker
+ deployment_configuration = each.value.deployment_configuration
deployment_controller = each.value.deployment_controller
deployment_maximum_percent = each.value.deployment_maximum_percent
deployment_minimum_healthy_percent = each.value.deployment_minimum_healthy_percent
diff --git a/modules/cluster/README.md b/modules/cluster/README.md
index c44adee..06f4bd8 100644
--- a/modules/cluster/README.md
+++ b/modules/cluster/README.md
@@ -135,13 +135,13 @@ module "ecs_cluster" {
| Name | Version |
|------|---------|
| [terraform](#requirement\_terraform) | >= 1.5.7 |
-| [aws](#requirement\_aws) | >= 6.0 |
+| [aws](#requirement\_aws) | >= 6.4 |
## Providers
| Name | Version |
|------|---------|
-| [aws](#provider\_aws) | >= 6.0 |
+| [aws](#provider\_aws) | >= 6.4 |
## Modules
diff --git a/modules/cluster/versions.tf b/modules/cluster/versions.tf
index db13b0a..497e3e6 100644
--- a/modules/cluster/versions.tf
+++ b/modules/cluster/versions.tf
@@ -4,7 +4,7 @@ terraform {
required_providers {
aws = {
source = "hashicorp/aws"
- version = ">= 6.0"
+ version = ">= 6.4"
}
}
}
diff --git a/modules/container-definition/README.md b/modules/container-definition/README.md
index 2fc6448..665c1c4 100644
--- a/modules/container-definition/README.md
+++ b/modules/container-definition/README.md
@@ -116,13 +116,13 @@ module "example_ecs_container_definition" {
| Name | Version |
|------|---------|
| [terraform](#requirement\_terraform) | >= 1.5.7 |
-| [aws](#requirement\_aws) | >= 6.0 |
+| [aws](#requirement\_aws) | >= 6.4 |
## Providers
| Name | Version |
|------|---------|
-| [aws](#provider\_aws) | >= 6.0 |
+| [aws](#provider\_aws) | >= 6.4 |
## Modules
diff --git a/modules/container-definition/versions.tf b/modules/container-definition/versions.tf
index db13b0a..497e3e6 100644
--- a/modules/container-definition/versions.tf
+++ b/modules/container-definition/versions.tf
@@ -4,7 +4,7 @@ terraform {
required_providers {
aws = {
source = "hashicorp/aws"
- version = ">= 6.0"
+ version = ">= 6.4"
}
}
}
diff --git a/modules/service/README.md b/modules/service/README.md
index 70f4528..babf5fc 100644
--- a/modules/service/README.md
+++ b/modules/service/README.md
@@ -170,13 +170,13 @@ module "ecs_service" {
| Name | Version |
|------|---------|
| [terraform](#requirement\_terraform) | >= 1.5.7 |
-| [aws](#requirement\_aws) | >= 6.0 |
+| [aws](#requirement\_aws) | >= 6.4 |
## Providers
| Name | Version |
|------|---------|
-| [aws](#provider\_aws) | >= 6.0 |
+| [aws](#provider\_aws) | >= 6.4 |
## Modules
@@ -250,6 +250,7 @@ module "ecs_service" {
| [create\_task\_exec\_policy](#input\_create\_task\_exec\_policy) | Determines whether the ECS task definition IAM policy should be created. This includes permissions included in AmazonECSTaskExecutionRolePolicy as well as access to secrets and SSM parameters | `bool` | `true` | no |
| [create\_tasks\_iam\_role](#input\_create\_tasks\_iam\_role) | Determines whether the ECS tasks IAM role should be created | `bool` | `true` | no |
| [deployment\_circuit\_breaker](#input\_deployment\_circuit\_breaker) | Configuration block for deployment circuit breaker | object({
enable = bool
rollback = bool
}) | `null` | no |
+| [deployment\_configuration](#input\_deployment\_configuration) | Configuration block for deployment settings | object({
strategy = optional(string)
bake_time_in_minutes = optional(string)
lifecycle_hook = optional(map(object({
hook_target_arn = string
role_arn = string
lifecycle_stages = list(string)
})))
}) | `null` | no |
| [deployment\_controller](#input\_deployment\_controller) | Configuration block for deployment controller configuration | object({
type = optional(string)
}) | `null` | no |
| [deployment\_maximum\_percent](#input\_deployment\_maximum\_percent) | Upper limit (as a percentage of the service's `desired_count`) of the number of running tasks that can be running in a service during a deployment | `number` | `200` | no |
| [deployment\_minimum\_healthy\_percent](#input\_deployment\_minimum\_healthy\_percent) | Lower limit (as a percentage of the service's `desired_count`) of the number of running tasks that must remain running and healthy in a service during a deployment | `number` | `66` | no |
@@ -282,7 +283,7 @@ module "ecs_service" {
| [infrastructure\_iam\_role\_use\_name\_prefix](#input\_infrastructure\_iam\_role\_use\_name\_prefix) | Determines whether the IAM role name (`iam_role_name`) is used as a prefix | `bool` | `true` | 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 |
| [launch\_type](#input\_launch\_type) | Launch type on which to run your service. The valid values are `EC2`, `FARGATE`, and `EXTERNAL`. Defaults to `FARGATE` | `string` | `"FARGATE"` | no |
-| [load\_balancer](#input\_load\_balancer) | Configuration block for load balancers | map(object({
container_name = string
container_port = number
elb_name = optional(string)
target_group_arn = optional(string)
})) | `null` | no |
+| [load\_balancer](#input\_load\_balancer) | Configuration block for load balancers | map(object({
container_name = string
container_port = number
elb_name = optional(string)
target_group_arn = optional(string)
advanced_configuration = optional(object({
alternate_target_group_arn = string
production_listener_rule = string
role_arn = string
test_listener_rule = optional(string)
}))
})) | `null` | no |
| [memory](#input\_memory) | Amount (in MiB) of memory used by the task. If the `requires_compatibilities` is `FARGATE` this field is required | `number` | `2048` | no |
| [name](#input\_name) | Name of the service (up to 255 letters, numbers, hyphens, and underscores) | `string` | `null` | no |
| [network\_mode](#input\_network\_mode) | Docker networking mode to use for the containers in the task. Valid values are `none`, `bridge`, `awsvpc`, and `host` | `string` | `"awsvpc"` | no |
@@ -304,7 +305,7 @@ module "ecs_service" {
| [security\_group\_name](#input\_security\_group\_name) | Name to use on security group created | `string` | `null` | no |
| [security\_group\_tags](#input\_security\_group\_tags) | A map of additional tags to add to the security group created | `map(string)` | `{}` | no |
| [security\_group\_use\_name\_prefix](#input\_security\_group\_use\_name\_prefix) | Determines whether the security group name (`security_group_name`) is used as a prefix | `bool` | `true` | no |
-| [service\_connect\_configuration](#input\_service\_connect\_configuration) | The ECS Service Connect configuration for this service to discover and connect to services, and be discovered by, and connected from, other services within a namespace | object({
enabled = optional(bool, true)
log_configuration = optional(object({
log_driver = string
options = optional(map(string))
secret_option = optional(list(object({
name = string
value_from = string
})))
}))
namespace = optional(string)
service = optional(list(object({
client_alias = optional(object({
dns_name = optional(string)
port = number
}))
discovery_name = optional(string)
ingress_port_override = optional(number)
port_name = string
timeout = optional(object({
idle_timeout_seconds = optional(number)
per_request_timeout_seconds = optional(number)
}))
tls = optional(object({
issuer_cert_authority = object({
aws_pca_authority_arn = string
})
kms_key = optional(string)
role_arn = optional(string)
}))
})))
}) | `null` | no |
+| [service\_connect\_configuration](#input\_service\_connect\_configuration) | The ECS Service Connect configuration for this service to discover and connect to services, and be discovered by, and connected from, other services within a namespace | object({
enabled = optional(bool, true)
log_configuration = optional(object({
log_driver = string
options = optional(map(string))
secret_option = optional(list(object({
name = string
value_from = string
})))
}))
namespace = optional(string)
service = optional(list(object({
client_alias = optional(object({
dns_name = optional(string)
port = number
test_traffic_rules = optional(list(object({
header = optional(object({
name = string
value = object({
exact = string
})
}))
})))
}))
discovery_name = optional(string)
ingress_port_override = optional(number)
port_name = string
timeout = optional(object({
idle_timeout_seconds = optional(number)
per_request_timeout_seconds = optional(number)
}))
tls = optional(object({
issuer_cert_authority = object({
aws_pca_authority_arn = string
})
kms_key = optional(string)
role_arn = optional(string)
}))
})))
}) | `null` | no |
| [service\_registries](#input\_service\_registries) | Service discovery registries for the service | object({
container_name = optional(string)
container_port = optional(number)
port = optional(number)
registry_arn = string
}) | `null` | no |
| [service\_tags](#input\_service\_tags) | A map of additional tags to add to the service | `map(string)` | `{}` | no |
| [skip\_destroy](#input\_skip\_destroy) | If true, the task is not deleted when the service is deleted | `bool` | `null` | no |
diff --git a/modules/service/main.tf b/modules/service/main.tf
index b3fc157..6119c86 100644
--- a/modules/service/main.tf
+++ b/modules/service/main.tf
@@ -73,6 +73,25 @@ resource "aws_ecs_service" "this" {
}
}
+ dynamic "deployment_configuration" {
+ for_each = var.deployment_configuration != null ? [var.deployment_configuration] : []
+
+ content {
+ strategy = deployment_configuration.value.strategy
+ bake_time_in_minutes = deployment_configuration.value.bake_time_in_minutes
+
+ dynamic "lifecycle_hook" {
+ for_each = deployment_configuration.value.lifecycle_hook != null ? deployment_configuration.value.lifecycle_hook : {}
+
+ content {
+ hook_target_arn = lifecycle_hook.value.hook_target_arn
+ role_arn = lifecycle_hook.value.role_arn
+ lifecycle_stages = lifecycle_hook.value.lifecycle_stages
+ }
+ }
+ }
+ }
+
dynamic "deployment_controller" {
for_each = var.deployment_controller != null ? [var.deployment_controller] : []
@@ -101,6 +120,17 @@ resource "aws_ecs_service" "this" {
container_port = load_balancer.value.container_port
elb_name = load_balancer.value.elb_name
target_group_arn = load_balancer.value.target_group_arn
+
+ dynamic "advanced_configuration" {
+ for_each = load_balancer.value.advanced_configuration != null ? [load_balancer.value.advanced_configuration] : []
+
+ content {
+ alternate_target_group_arn = advanced_configuration.value.alternate_target_group_arn
+ production_listener_rule = advanced_configuration.value.production_listener_rule
+ role_arn = advanced_configuration.value.role_arn
+ test_listener_rule = advanced_configuration.value.test_listener_rule
+ }
+ }
}
}
@@ -176,6 +206,28 @@ resource "aws_ecs_service" "this" {
content {
dns_name = client_alias.value.dns_name
port = client_alias.value.port
+
+ dynamic "test_traffic_rules" {
+ for_each = client_alias.value.test_traffic_rules != null ? client_alias.value.test_traffic_rules : []
+
+ content {
+ dynamic "header" {
+ for_each = test_traffic_rules.value.header != null ? [test_traffic_rules.value.header] : []
+
+ content {
+ name = header.value.name
+
+ dynamic "value" {
+ for_each = header.value.value != null ? [header.value.value] : []
+
+ content {
+ exact = value.value.exact
+ }
+ }
+ }
+ }
+ }
+ }
}
}
@@ -341,6 +393,25 @@ resource "aws_ecs_service" "ignore_task_definition" {
}
}
+ dynamic "deployment_configuration" {
+ for_each = var.deployment_configuration != null ? [var.deployment_configuration] : []
+
+ content {
+ strategy = deployment_configuration.value.strategy
+ bake_time_in_minutes = deployment_configuration.value.bake_time_in_minutes
+
+ dynamic "lifecycle_hook" {
+ for_each = deployment_configuration.value.lifecycle_hook != null ? deployment_configuration.value.lifecycle_hook : {}
+
+ content {
+ hook_target_arn = lifecycle_hook.value.hook_target_arn
+ role_arn = lifecycle_hook.value.role_arn
+ lifecycle_stages = lifecycle_hook.value.lifecycle_stages
+ }
+ }
+ }
+ }
+
dynamic "deployment_controller" {
for_each = var.deployment_controller != null ? [var.deployment_controller] : []
@@ -369,6 +440,17 @@ resource "aws_ecs_service" "ignore_task_definition" {
container_port = load_balancer.value.container_port
elb_name = load_balancer.value.elb_name
target_group_arn = load_balancer.value.target_group_arn
+
+ dynamic "advanced_configuration" {
+ for_each = load_balancer.value.advanced_configuration != null ? [load_balancer.value.advanced_configuration] : []
+
+ content {
+ alternate_target_group_arn = advanced_configuration.value.alternate_target_group_arn
+ production_listener_rule = advanced_configuration.value.production_listener_rule
+ role_arn = advanced_configuration.value.role_arn
+ test_listener_rule = advanced_configuration.value.test_listener_rule
+ }
+ }
}
}
@@ -444,6 +526,28 @@ resource "aws_ecs_service" "ignore_task_definition" {
content {
dns_name = client_alias.value.dns_name
port = client_alias.value.port
+
+ dynamic "test_traffic_rules" {
+ for_each = client_alias.value.test_traffic_rules != null ? client_alias.value.test_traffic_rules : []
+
+ content {
+ dynamic "header" {
+ for_each = test_traffic_rules.value.header != null ? [test_traffic_rules.value.header] : []
+
+ content {
+ name = header.value.name
+
+ dynamic "value" {
+ for_each = header.value.value != null ? [header.value.value] : []
+
+ content {
+ exact = value.value.exact
+ }
+ }
+ }
+ }
+ }
+ }
}
}
diff --git a/modules/service/variables.tf b/modules/service/variables.tf
index f007632..0a49c7a 100644
--- a/modules/service/variables.tf
+++ b/modules/service/variables.tf
@@ -73,6 +73,20 @@ variable "deployment_circuit_breaker" {
default = null
}
+variable "deployment_configuration" {
+ description = "Configuration block for deployment settings"
+ type = object({
+ strategy = optional(string)
+ bake_time_in_minutes = optional(string)
+ lifecycle_hook = optional(map(object({
+ hook_target_arn = string
+ role_arn = string
+ lifecycle_stages = list(string)
+ })))
+ })
+ default = null
+}
+
variable "deployment_controller" {
description = "Configuration block for deployment controller configuration"
type = object({
@@ -142,6 +156,12 @@ variable "load_balancer" {
container_port = number
elb_name = optional(string)
target_group_arn = optional(string)
+ advanced_configuration = optional(object({
+ alternate_target_group_arn = string
+ production_listener_rule = string
+ role_arn = string
+ test_listener_rule = optional(string)
+ }))
}))
default = null
}
@@ -223,6 +243,14 @@ variable "service_connect_configuration" {
client_alias = optional(object({
dns_name = optional(string)
port = number
+ test_traffic_rules = optional(list(object({
+ header = optional(object({
+ name = string
+ value = object({
+ exact = string
+ })
+ }))
+ })))
}))
discovery_name = optional(string)
ingress_port_override = optional(number)
diff --git a/modules/service/versions.tf b/modules/service/versions.tf
index db13b0a..497e3e6 100644
--- a/modules/service/versions.tf
+++ b/modules/service/versions.tf
@@ -4,7 +4,7 @@ terraform {
required_providers {
aws = {
source = "hashicorp/aws"
- version = ">= 6.0"
+ version = ">= 6.4"
}
}
}
diff --git a/variables.tf b/variables.tf
index 6122006..47b5928 100644
--- a/variables.tf
+++ b/variables.tf
@@ -280,6 +280,15 @@ variable "services" {
enable = bool
rollback = bool
}))
+ deployment_configuration = optional(object({
+ strategy = optional(string)
+ bake_time_in_minutes = optional(string)
+ lifecycle_hook = optional(map(object({
+ hook_target_arn = string
+ role_arn = string
+ lifecycle_stages = list(string)
+ })))
+ }))
deployment_controller = optional(object({
type = optional(string)
}))
@@ -297,6 +306,12 @@ variable "services" {
container_port = number
elb_name = optional(string)
target_group_arn = optional(string)
+ advanced_configuration = optional(object({
+ alternate_target_group_arn = string
+ production_listener_rule = string
+ role_arn = string
+ test_listener_rule = optional(string)
+ }))
})))
name = optional(string) # Will fall back to use map key if not set
assign_public_ip = optional(bool, false)
@@ -328,6 +343,14 @@ variable "services" {
client_alias = optional(object({
dns_name = optional(string)
port = number
+ test_traffic_rules = optional(list(object({
+ header = optional(object({
+ name = string
+ value = object({
+ exact = string
+ })
+ }))
+ })))
}))
discovery_name = optional(string)
ingress_port_override = optional(number)
diff --git a/versions.tf b/versions.tf
index db13b0a..497e3e6 100644
--- a/versions.tf
+++ b/versions.tf
@@ -4,7 +4,7 @@ terraform {
required_providers {
aws = {
source = "hashicorp/aws"
- version = ">= 6.0"
+ version = ">= 6.4"
}
}
}
diff --git a/wrappers/cluster/versions.tf b/wrappers/cluster/versions.tf
index db13b0a..497e3e6 100644
--- a/wrappers/cluster/versions.tf
+++ b/wrappers/cluster/versions.tf
@@ -4,7 +4,7 @@ terraform {
required_providers {
aws = {
source = "hashicorp/aws"
- version = ">= 6.0"
+ version = ">= 6.4"
}
}
}
diff --git a/wrappers/container-definition/versions.tf b/wrappers/container-definition/versions.tf
index db13b0a..497e3e6 100644
--- a/wrappers/container-definition/versions.tf
+++ b/wrappers/container-definition/versions.tf
@@ -4,7 +4,7 @@ terraform {
required_providers {
aws = {
source = "hashicorp/aws"
- version = ">= 6.0"
+ version = ">= 6.4"
}
}
}
diff --git a/wrappers/service/main.tf b/wrappers/service/main.tf
index 96bdbbe..8cb3d9d 100644
--- a/wrappers/service/main.tf
+++ b/wrappers/service/main.tf
@@ -43,6 +43,7 @@ module "wrapper" {
create_task_exec_policy = try(each.value.create_task_exec_policy, var.defaults.create_task_exec_policy, true)
create_tasks_iam_role = try(each.value.create_tasks_iam_role, var.defaults.create_tasks_iam_role, true)
deployment_circuit_breaker = try(each.value.deployment_circuit_breaker, var.defaults.deployment_circuit_breaker, null)
+ deployment_configuration = try(each.value.deployment_configuration, var.defaults.deployment_configuration, null)
deployment_controller = try(each.value.deployment_controller, var.defaults.deployment_controller, null)
deployment_maximum_percent = try(each.value.deployment_maximum_percent, var.defaults.deployment_maximum_percent, 200)
deployment_minimum_healthy_percent = try(each.value.deployment_minimum_healthy_percent, var.defaults.deployment_minimum_healthy_percent, 66)
diff --git a/wrappers/service/versions.tf b/wrappers/service/versions.tf
index db13b0a..497e3e6 100644
--- a/wrappers/service/versions.tf
+++ b/wrappers/service/versions.tf
@@ -4,7 +4,7 @@ terraform {
required_providers {
aws = {
source = "hashicorp/aws"
- version = ">= 6.0"
+ version = ">= 6.4"
}
}
}
diff --git a/wrappers/versions.tf b/wrappers/versions.tf
index db13b0a..497e3e6 100644
--- a/wrappers/versions.tf
+++ b/wrappers/versions.tf
@@ -4,7 +4,7 @@ terraform {
required_providers {
aws = {
source = "hashicorp/aws"
- version = ">= 6.0"
+ version = ">= 6.4"
}
}
}