From 356b6c28f9e3fc123bc44cc25ce99dbc7b2f8329 Mon Sep 17 00:00:00 2001 From: Bryant Biggs Date: Wed, 6 Aug 2025 10:02:50 -0500 Subject: [PATCH 1/3] fix: Correct variable defaults and container definition conditional logic --- README.md | 2 +- examples/container-definition/README.md | 6 +- examples/container-definition/outputs.tf | 13 +- examples/container-definition/versions.tf | 6 +- examples/fargate/main.tf | 22 +-- modules/container-definition/README.md | 6 +- modules/container-definition/main.tf | 13 +- modules/container-definition/outputs.tf | 3 +- modules/container-definition/variables.tf | 42 ++++-- modules/service/README.md | 2 +- modules/service/variables.tf | 94 +++++++++---- variables.tf | 156 +++++++++------------- wrappers/container-definition/main.tf | 30 ++--- 13 files changed, 216 insertions(+), 179 deletions(-) diff --git a/README.md b/README.md index 394d4c1..2c88c29 100644 --- a/README.md +++ b/README.md @@ -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_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 | +| [services](#input\_services) | Map of service definitions to create |
map(object({
create = optional(bool)
create_service = optional(bool)
tags = optional(map(string))

# Service
ignore_task_definition_changes = optional(bool)
alarms = optional(object({
alarm_names = list(string)
enable = optional(bool)
rollback = optional(bool)
}))
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)
enable_execute_command = optional(bool)
force_delete = optional(bool)
force_new_deployment = optional(bool)
health_check_grace_period_seconds = optional(number)
launch_type = optional(string)
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)
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)
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)
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)
iam_role_arn = optional(string)
iam_role_name = optional(string)
iam_role_use_name_prefix = optional(bool)
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)
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)
create_cloudwatch_log_group = optional(bool)
cloudwatch_log_group_name = optional(string)
cloudwatch_log_group_use_name_prefix = optional(bool)
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)
pid_mode = optional(string)
proxy_configuration = optional(object({
container_name = string
properties = optional(map(string))
type = optional(string)
}))
requires_compatibilities = optional(list(string))
runtime_platform = optional(object({
cpu_architecture = optional(string)
operating_system_family = optional(string)
}))
skip_destroy = optional(bool)
task_definition_placement_constraints = optional(map(object({
expression = optional(string)
type = string
})))
track_latest = optional(bool)
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)
task_exec_iam_role_arn = optional(string)
task_exec_iam_role_name = optional(string)
task_exec_iam_role_use_name_prefix = optional(bool)
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)
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)
tasks_iam_role_arn = optional(string)
tasks_iam_role_name = optional(string)
tasks_iam_role_use_name_prefix = optional(bool)
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)
autoscaling_min_capacity = optional(number)
autoscaling_max_capacity = optional(number)
autoscaling_policies = optional(map(object({
name = optional(string) # Will fall back to the key name if not provided
policy_type = optional(string)
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)
scale_out_cooldown = optional(number)
target_value = optional(number)
}))
})))
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)
security_group_name = optional(string)
security_group_use_name_prefix = optional(bool)
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)
prefix_list_id = optional(string)
referenced_security_group_id = optional(string)
tags = optional(map(string))
to_port = optional(string)
})))
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)
prefix_list_id = optional(string)
referenced_security_group_id = optional(string)
tags = optional(map(string))
to_port = optional(string)
})))
security_group_tags = optional(map(string))
# ECS Infrastructure IAM Role
create_infrastructure_iam_role = optional(bool)
infrastructure_iam_role_arn = optional(string)
infrastructure_iam_role_name = optional(string)
infrastructure_iam_role_use_name_prefix = optional(bool)
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/container-definition/README.md b/examples/container-definition/README.md index 2a5ab63..0da617a 100644 --- a/examples/container-definition/README.md +++ b/examples/container-definition/README.md @@ -23,13 +23,13 @@ Note that this example may create resources which will incur monetary charges on |------|---------| | [terraform](#requirement\_terraform) | >= 1.5.7 | | [aws](#requirement\_aws) | >= 6.4 | -| [local](#requirement\_local) | >= 2.5 | +| [null](#requirement\_null) | >= 3.2 | ## Providers | Name | Version | |------|---------| -| [local](#provider\_local) | >= 2.5 | +| [null](#provider\_null) | >= 3.2 | ## Modules @@ -41,7 +41,7 @@ Note that this example may create resources which will incur monetary charges on | Name | Type | |------|------| -| [local_file.container_definition_json](https://registry.terraform.io/providers/hashicorp/local/latest/docs/resources/file) | resource | +| [null_resource.container_definition_json](https://registry.terraform.io/providers/hashicorp/null/latest/docs/resources/resource) | resource | ## Inputs diff --git a/examples/container-definition/outputs.tf b/examples/container-definition/outputs.tf index dbf1f05..18eae3f 100644 --- a/examples/container-definition/outputs.tf +++ b/examples/container-definition/outputs.tf @@ -12,9 +12,16 @@ output "container_definition_json" { value = module.ecs_container_definition.container_definition_json } -resource "local_file" "container_definition_json" { - content = module.ecs_container_definition.container_definition_json - filename = "${path.module}/definition.json" +# Need the output pretty-printed and sorted for comparison +resource "null_resource" "container_definition_json" { + triggers = { + container_definition_json = timestamp() + } + + provisioner "local-exec" { + # Bootstrap script called with private_ip of each node in the cluster + command = "echo '${module.ecs_container_definition.container_definition_json}' | jq -S > ./definition.json" + } } ################################################################################ diff --git a/examples/container-definition/versions.tf b/examples/container-definition/versions.tf index 9efd186..acee281 100644 --- a/examples/container-definition/versions.tf +++ b/examples/container-definition/versions.tf @@ -6,9 +6,9 @@ terraform { source = "hashicorp/aws" version = ">= 6.4" } - local = { - source = "hashicorp/local" - version = ">= 2.5" + null = { + source = "hashicorp/null" + version = ">= 3.2" } } } diff --git a/examples/fargate/main.tf b/examples/fargate/main.tf index f52e2c6..21251ad 100644 --- a/examples/fargate/main.tf +++ b/examples/fargate/main.tf @@ -65,18 +65,18 @@ module "ecs_service" { strategy = "BLUE_GREEN" bake_time_in_minutes = 2 - # example config using lifecycle hooks + # # 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"] - # } + # 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"] + # } # } } diff --git a/modules/container-definition/README.md b/modules/container-definition/README.md index db723f5..7b2109e 100644 --- a/modules/container-definition/README.md +++ b/modules/container-definition/README.md @@ -166,7 +166,7 @@ No modules. | [image](#input\_image) | The image used to start a container. This string is passed directly to the Docker daemon. By default, images in the Docker Hub registry are available. Other repositories are specified with either `repository-url/image:tag` or `repository-url/image@digest` | `string` | `null` | no | | [interactive](#input\_interactive) | When this parameter is `true`, you can deploy containerized applications that require `stdin` or a `tty` to be allocated | `bool` | `false` | no | | [links](#input\_links) | The links parameter allows containers to communicate with each other without the need for port mappings. This parameter is only supported if the network mode of a task definition is `bridge` | `list(string)` | `null` | no | -| [linuxParameters](#input\_linuxParameters) | Linux-specific modifications that are applied to the container, such as Linux kernel capabilities. For more information see [KernelCapabilities](https://docs.aws.amazon.com/AmazonECS/latest/APIReference/API_KernelCapabilities.html) |
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, false)
maxSwap = optional(number)
sharedMemorySize = optional(number)
swappiness = optional(number)
tmpfs = optional(list(object({
containerPath = string
mountOptions = optional(list(string))
size = number
})))
})
|
{
"initProcessEnabled": false
}
| no | +| [linuxParameters](#input\_linuxParameters) | Linux-specific modifications that are applied to the container, such as Linux kernel capabilities. For more information see [KernelCapabilities](https://docs.aws.amazon.com/AmazonECS/latest/APIReference/API_KernelCapabilities.html) |
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
})))
})
| `{}` | no | | [logConfiguration](#input\_logConfiguration) | The log configuration for the container. For more information see [LogConfiguration](https://docs.aws.amazon.com/AmazonECS/latest/APIReference/API_LogConfiguration.html) |
object({
logDriver = optional(string)
options = optional(map(string))
secretOptions = optional(list(object({
name = string
valueFrom = string
})))
})
| `{}` | no | | [memory](#input\_memory) | The amount (in MiB) of memory to present to the container. If your container attempts to exceed the memory specified here, the container is killed. The total amount of memory reserved for all containers within a task must be lower than the task `memory` value, if one is specified | `number` | `null` | no | | [memoryReservation](#input\_memoryReservation) | The soft limit (in MiB) of memory to reserve for the container. When system memory is under heavy contention, Docker attempts to keep the container memory to this soft limit. However, your container can consume more memory when it needs to, up to either the hard limit specified with the `memory` parameter (if applicable), or all of the available memory on the container instance | `number` | `null` | no | @@ -180,7 +180,7 @@ No modules. | [region](#input\_region) | Region where the resource(s) will be managed. Defaults to the Region set in the provider configuration | `string` | `null` | no | | [repositoryCredentials](#input\_repositoryCredentials) | Container repository credentials; required when using a private repo. This map currently supports a single key; "credentialsParameter", which should be the ARN of a Secrets Manager's secret holding the credentials |
object({
credentialsParameter = optional(string)
})
| `null` | no | | [resourceRequirements](#input\_resourceRequirements) | The type and amount of a resource to assign to a container. The only supported resource is a GPU |
list(object({
type = string
value = string
}))
| `null` | no | -| [restartPolicy](#input\_restartPolicy) | Container restart policy; helps overcome transient failures faster and maintain task availability |
object({
enabled = optional(bool, true)
ignoredExitCodes = optional(list(number), [])
restartAttemptPeriod = optional(number)
})
|
{
"enabled": true
}
| no | +| [restartPolicy](#input\_restartPolicy) | Container restart policy; helps overcome transient failures faster and maintain task availability |
object({
enabled = optional(bool)
ignoredExitCodes = optional(list(number))
restartAttemptPeriod = optional(number)
})
|
{
"enabled": true
}
| no | | [secrets](#input\_secrets) | The secrets to pass to the container. For more information, see [Specifying Sensitive Data](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/specifying-sensitive-data.html) in the Amazon Elastic Container Service Developer Guide |
list(object({
name = string
valueFrom = string
}))
| `null` | no | | [service](#input\_service) | The name of the service that the container definition is associated with. Used in CloudWatch log group default name (if one is not provided) | `string` | `null` | no | | [startTimeout](#input\_startTimeout) | Time duration (in seconds) to wait before giving up on resolving dependencies for a container | `number` | `30` | no | @@ -200,7 +200,7 @@ No modules. | [cloudwatch\_log\_group\_arn](#output\_cloudwatch\_log\_group\_arn) | ARN of CloudWatch log group created | | [cloudwatch\_log\_group\_name](#output\_cloudwatch\_log\_group\_name) | Name of CloudWatch log group created | | [container\_definition](#output\_container\_definition) | Container definition | -| [container\_definition\_json](#output\_container\_definition\_json) | Container definition | +| [container\_definition\_json](#output\_container\_definition\_json) | Container definition. NOTE: use `jsonencode([module.ecs_container_definition.container_definition])` instead of this output when passing into a Task Definition | ## License diff --git a/modules/container-definition/main.tf b/modules/container-definition/main.tf index edd1031..d38eb83 100644 --- a/modules/container-definition/main.tf +++ b/modules/container-definition/main.tf @@ -2,7 +2,6 @@ data "aws_region" "current" { region = var.region } - locals { is_not_windows = contains(["LINUX"], var.operating_system_family) @@ -23,8 +22,16 @@ locals { { for k, v in var.logConfiguration : k => v if v != null } ) + # 1. We remove any attributes that are set to `null` by default from the variable optional attributes + # tflint-ignore: terraform_naming_convention + trimedLinuxParameters = { for k, v in var.linuxParameters : k => v if v != null } + # 2. We then merge in the `initProcessEnabled` attribute based on whether `enable_execute_command` is true or false + # This also means we will always have something in `linuxParameters` (it will never be `null` or `{}`) + # Terraform doesn't allow us to set `initProcessEnabled` to `true` on one side only of the conditional, so we have to merge it in on both sides + # However, in the `true` case, we set it last to ensure `initProcessEnabled` is always `true` when `enable_execute_command` is true + # and the "psuedo-default" is `false` when `enable_execute_command` is false (but can still be overridden by the user) # tflint-ignore: terraform_naming_convention - linuxParameters = var.enable_execute_command ? merge({ "initProcessEnabled" : true }, var.linuxParameters) : merge({ "initProcessEnabled" : false }, var.linuxParameters) + linuxParameters = var.enable_execute_command ? merge(local.trimedLinuxParameters, { "initProcessEnabled" : true }) : merge({ "initProcessEnabled" : false }, local.trimedLinuxParameters) definition = { command = var.command @@ -46,7 +53,7 @@ locals { image = var.image interactive = var.interactive links = local.is_not_windows ? var.links : null - linuxParameters = local.is_not_windows ? { for k, v in local.linuxParameters : k => v if v != null } : null + linuxParameters = local.is_not_windows ? local.linuxParameters : null logConfiguration = length(local.logConfiguration) > 0 ? local.logConfiguration : null memory = var.memory memoryReservation = var.memoryReservation diff --git a/modules/container-definition/outputs.tf b/modules/container-definition/outputs.tf index ea93810..efa82e1 100644 --- a/modules/container-definition/outputs.tf +++ b/modules/container-definition/outputs.tf @@ -7,8 +7,9 @@ output "container_definition" { value = local.container_definition } +# ToDo - remove at next breaking change. Not worth it output "container_definition_json" { - description = "Container definition" + description = "Container definition. NOTE: use `jsonencode([module.ecs_container_definition.container_definition])` instead of this output when passing into a Task Definition" value = jsonencode(local.container_definition) } diff --git a/modules/container-definition/variables.tf b/modules/container-definition/variables.tf index 82e451b..86f8165 100644 --- a/modules/container-definition/variables.tf +++ b/modules/container-definition/variables.tf @@ -8,12 +8,14 @@ variable "operating_system_family" { description = "The OS family for task" type = string default = "LINUX" + nullable = false } variable "tags" { description = "A map of tags to add to all resources" type = map(string) default = {} + nullable = false } ################################################################################ @@ -81,12 +83,14 @@ variable "enable_execute_command" { description = "Specifies whether to enable Amazon ECS Exec for the tasks within the service" type = bool default = false + nullable = false } variable "entrypoint" { description = "The entry point that is passed to the container" type = list(string) default = [] + nullable = false } variable "environment" { @@ -95,7 +99,8 @@ variable "environment" { name = string value = string })) - default = [] + default = [] + nullable = false } # tflint-ignore: terraform_naming_convention @@ -105,7 +110,8 @@ variable "environmentFiles" { value = string type = string })) - default = [] + default = [] + nullable = false } variable "essential" { @@ -163,6 +169,7 @@ variable "interactive" { description = "When this parameter is `true`, you can deploy containerized applications that require `stdin` or a `tty` to be allocated" type = bool default = false + nullable = false } variable "links" { @@ -184,7 +191,7 @@ variable "linuxParameters" { hostPath = optional(string) permissions = optional(list(string)) }))) - initProcessEnabled = optional(bool, false) + initProcessEnabled = optional(bool) maxSwap = optional(number) sharedMemorySize = optional(number) swappiness = optional(number) @@ -194,9 +201,8 @@ variable "linuxParameters" { size = number }))) }) - default = { - initProcessEnabled = false - } + default = {} + nullable = false } # tflint-ignore: terraform_naming_convention @@ -210,7 +216,8 @@ variable "logConfiguration" { valueFrom = string }))) }) - default = {} + default = {} + nullable = false } variable "memory" { @@ -234,7 +241,8 @@ variable "mountPoints" { readOnly = optional(bool) sourceVolume = optional(string) })) - default = [] + default = [] + nullable = false } variable "name" { @@ -261,6 +269,7 @@ variable "privileged" { description = "When this parameter is true, the container is given elevated privileges on the host container instance (similar to the root user)" type = bool default = false + nullable = false } # tflint-ignore: terraform_naming_convention @@ -268,6 +277,7 @@ variable "pseudoTerminal" { description = "When this parameter is true, a `TTY` is allocated" type = bool default = false + nullable = false } # tflint-ignore: terraform_naming_convention @@ -275,6 +285,7 @@ variable "readonlyRootFilesystem" { description = "When this parameter is true, the container is given read-only access to its root file system" type = bool default = true + nullable = false } # tflint-ignore: terraform_naming_convention @@ -300,8 +311,8 @@ variable "resourceRequirements" { variable "restartPolicy" { description = "Container restart policy; helps overcome transient failures faster and maintain task availability" type = object({ - enabled = optional(bool, true) - ignoredExitCodes = optional(list(number), []) + enabled = optional(bool) + ignoredExitCodes = optional(list(number)) restartAttemptPeriod = optional(number) }) default = { @@ -339,7 +350,8 @@ variable "systemControls" { namespace = optional(string) value = optional(string) })) - default = [] + default = [] + nullable = false } variable "ulimits" { @@ -363,6 +375,7 @@ variable "versionConsistency" { description = "Specifies whether Amazon ECS will resolve the container image tag provided in the container definition to an image digest" type = string default = "disabled" + nullable = false } # tflint-ignore: terraform_naming_convention @@ -372,7 +385,8 @@ variable "volumesFrom" { readOnly = optional(bool) sourceContainer = optional(string) })) - default = [] + default = [] + nullable = false } # tflint-ignore: terraform_naming_convention @@ -396,12 +410,14 @@ variable "enable_cloudwatch_logging" { description = "Determines whether CloudWatch logging is configured for this container definition. Set to `false` to use other logging drivers" type = bool default = true + nullable = false } variable "create_cloudwatch_log_group" { description = "Determines whether a log group is created by this module. If not, AWS will automatically create one if logging is enabled" type = bool default = true + nullable = false } variable "cloudwatch_log_group_name" { @@ -414,6 +430,7 @@ variable "cloudwatch_log_group_use_name_prefix" { description = "Determines whether the log group name should be used as a prefix" type = bool default = false + nullable = false } variable "cloudwatch_log_group_class" { @@ -426,6 +443,7 @@ variable "cloudwatch_log_group_retention_in_days" { description = "Number of days to retain log events. Set to `0` to keep logs indefinitely" type = number default = 14 + nullable = false } variable "cloudwatch_log_group_kms_key_id" { diff --git a/modules/service/README.md b/modules/service/README.md index 49e6c16..d37ef35 100644 --- a/modules/service/README.md +++ b/modules/service/README.md @@ -238,7 +238,7 @@ module "ecs_service" { | [availability\_zone\_rebalancing](#input\_availability\_zone\_rebalancing) | ECS automatically redistributes tasks within a service across Availability Zones (AZs) to mitigate the risk of impaired application availability due to underlying infrastructure failures and task lifecycle activities. The valid values are `ENABLED` and `DISABLED`. Defaults to `DISABLED` | `string` | `null` | no | | [capacity\_provider\_strategy](#input\_capacity\_provider\_strategy) | Capacity provider strategies to use for the service. Can be one or more |
map(object({
base = optional(number)
capacity_provider = string
weight = optional(number)
}))
| `null` | no | | [cluster\_arn](#input\_cluster\_arn) | ARN of the ECS cluster where the resources will be provisioned | `string` | `""` | no | -| [container\_definitions](#input\_container\_definitions) | A map of valid [container definitions](http://docs.aws.amazon.com/AmazonECS/latest/APIReference/API_ContainerDefinition.html). Please note that you should only provide values that are part of the container definition document |
map(object({
create = optional(bool, true)
operating_system_family = optional(string, "LINUX")
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, false) Set in standalone variable
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, 30)
retries = optional(number, 3)
startPeriod = optional(number)
timeout = optional(number, 5)
}))
hostname = optional(string)
image = optional(string)
interactive = optional(bool, false)
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, false)
maxSwap = optional(number)
sharedMemorySize = optional(number)
swappiness = optional(number)
tmpfs = optional(list(object({
containerPath = string
mountOptions = optional(list(string))
size = number
})))
}),
# Default
{
initProcessEnabled = false
}
)
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, false)
pseudoTerminal = optional(bool, false)
readonlyRootFilesystem = optional(bool, true)
repositoryCredentials = optional(object({
credentialsParameter = optional(string)
}))
resourceRequirements = optional(list(object({
type = string
value = string
})))
restartPolicy = optional(object({
enabled = optional(bool, true)
ignoredExitCodes = optional(list(number))
restartAttemptPeriod = optional(number)
}),
# Default
{
enabled = true
}
)
secrets = optional(list(object({
name = string
valueFrom = string
})))
startTimeout = optional(number, 30)
stopTimeout = optional(number, 120)
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, "disabled")
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, 14)
cloudwatch_log_group_kms_key_id = optional(string)
}))
| `{}` | no | +| [container\_definitions](#input\_container\_definitions) | A map of valid [container definitions](http://docs.aws.amazon.com/AmazonECS/latest/APIReference/API_ContainerDefinition.html). Please note that you should only provide values that are part of the container definition document |
map(object({
create = optional(bool, true)
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, false) Set in standalone variable
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, 30)
retries = optional(number, 3)
startPeriod = optional(number)
timeout = optional(number, 5)
}))
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)
}),
# Default
{
enabled = true
}
)
secrets = optional(list(object({
name = string
valueFrom = string
})))
startTimeout = optional(number, 30)
stopTimeout = optional(number, 120)
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)
create_cloudwatch_log_group = optional(bool)
cloudwatch_log_group_name = optional(string)
cloudwatch_log_group_use_name_prefix = optional(bool)
cloudwatch_log_group_class = optional(string)
cloudwatch_log_group_retention_in_days = optional(number)
cloudwatch_log_group_kms_key_id = optional(string)
}))
| `{}` | no | | [cpu](#input\_cpu) | Number of cpu units used by the task. If the `requires_compatibilities` is `FARGATE` this field is required | `number` | `1024` | no | | [create](#input\_create) | Determines whether resources will be created (affects all resources) | `bool` | `true` | no | | [create\_iam\_role](#input\_create\_iam\_role) | Determines whether the ECS service IAM role should be created | `bool` | `true` | no | diff --git a/modules/service/variables.tf b/modules/service/variables.tf index 0df1372..121a3fe 100644 --- a/modules/service/variables.tf +++ b/modules/service/variables.tf @@ -2,12 +2,14 @@ variable "create" { description = "Determines whether resources will be created (affects all resources)" type = bool default = true + nullable = false } variable "create_service" { description = "Determines whether service resource will be created (set to `false` in case you want to create task definition only)" type = bool default = true + nullable = false } variable "region" { @@ -20,6 +22,7 @@ variable "tags" { description = "A map of tags to add to all resources" type = map(string) default = {} + nullable = false } ################################################################################ @@ -30,6 +33,7 @@ variable "ignore_task_definition_changes" { description = "Whether changes to service `task_definition` changes should be ignored" type = bool default = false + nullable = false } variable "alarms" { @@ -62,6 +66,7 @@ variable "cluster_arn" { description = "ARN of the ECS cluster where the resources will be provisioned" type = string default = "" + nullable = false } variable "deployment_circuit_breaker" { @@ -117,12 +122,14 @@ variable "enable_ecs_managed_tags" { description = "Specifies whether to enable Amazon ECS managed tags for the tasks within the service" type = bool default = true + nullable = false } variable "enable_execute_command" { description = "Specifies whether to enable Amazon ECS Exec for the tasks within the service" type = bool default = false + nullable = false } variable "force_delete" { @@ -135,6 +142,7 @@ variable "force_new_deployment" { description = "Enable to force a new task deployment of the service. This can be used to update tasks to use a newer Docker image with same image/tag combination, roll Fargate tasks onto a newer platform version, or immediately deploy `ordered_placement_strategy` and `placement_constraints` updates" type = bool default = true + nullable = false } variable "health_check_grace_period_seconds" { @@ -147,6 +155,7 @@ variable "launch_type" { description = "Launch type on which to run your service. The valid values are `EC2`, `FARGATE`, and `EXTERNAL`. Defaults to `FARGATE`" type = string default = "FARGATE" + nullable = false } variable "load_balancer" { @@ -176,18 +185,21 @@ variable "assign_public_ip" { description = "Assign a public IP address to the ENI (Fargate launch type only)" type = bool default = false + nullable = false } variable "security_group_ids" { description = "List of security groups to associate with the task or service" type = list(string) default = [] + nullable = false } variable "subnet_ids" { description = "List of subnets to associate with the task or service" type = list(string) default = [] + nullable = false } variable "ordered_placement_strategy" { @@ -341,6 +353,7 @@ variable "service_tags" { description = "A map of additional tags to add to the service" type = map(string) default = {} + nullable = false } ################################################################################ @@ -351,6 +364,7 @@ variable "create_iam_role" { description = "Determines whether the ECS service IAM role should be created" type = bool default = true + nullable = false } variable "iam_role_arn" { @@ -369,6 +383,7 @@ variable "iam_role_use_name_prefix" { description = "Determines whether the IAM role name (`iam_role_name`) is used as a prefix" type = bool default = true + nullable = false } variable "iam_role_path" { @@ -393,6 +408,7 @@ variable "iam_role_tags" { description = "A map of additional tags to add to the IAM role created" type = map(string) default = {} + nullable = false } variable "iam_role_statements" { @@ -429,6 +445,7 @@ variable "create_task_definition" { description = "Determines whether to create a task definition or use existing/provided" type = bool default = true + nullable = false } variable "task_definition_arn" { @@ -441,8 +458,8 @@ variable "container_definitions" { description = "A map of valid [container definitions](http://docs.aws.amazon.com/AmazonECS/latest/APIReference/API_ContainerDefinition.html). Please note that you should only provide values that are part of the container definition document" type = map(object({ create = optional(bool, true) - operating_system_family = optional(string, "LINUX") - tags = optional(map(string), {}) + operating_system_family = optional(string) + tags = optional(map(string)) # Container definition command = optional(list(string)) @@ -461,7 +478,7 @@ variable "container_definitions" { environment = optional(list(object({ name = string value = string - })), []) + }))) environmentFiles = optional(list(object({ type = string value = string @@ -484,7 +501,7 @@ variable "container_definitions" { })) hostname = optional(string) image = optional(string) - interactive = optional(bool, false) + interactive = optional(bool) links = optional(list(string)) linuxParameters = optional(object({ capabilities = optional(object({ @@ -496,7 +513,7 @@ variable "container_definitions" { hostPath = optional(string) permissions = optional(list(string)) }))) - initProcessEnabled = optional(bool, false) + initProcessEnabled = optional(bool) maxSwap = optional(number) sharedMemorySize = optional(number) swappiness = optional(number) @@ -505,12 +522,7 @@ variable "container_definitions" { mountOptions = optional(list(string)) size = number }))) - }), - # Default - { - initProcessEnabled = false - } - ) + })) logConfiguration = optional(object({ logDriver = optional(string) options = optional(map(string)) @@ -518,14 +530,14 @@ variable "container_definitions" { 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) @@ -535,9 +547,9 @@ variable "container_definitions" { name = optional(string) protocol = optional(string) }))) - privileged = optional(bool, false) - pseudoTerminal = optional(bool, false) - readonlyRootFilesystem = optional(bool, true) + privileged = optional(bool) + pseudoTerminal = optional(bool) + readonlyRootFilesystem = optional(bool) repositoryCredentials = optional(object({ credentialsParameter = optional(string) })) @@ -546,7 +558,7 @@ variable "container_definitions" { value = string }))) restartPolicy = optional(object({ - enabled = optional(bool, true) + enabled = optional(bool) ignoredExitCodes = optional(list(number)) restartAttemptPeriod = optional(number) }), @@ -564,28 +576,28 @@ variable "container_definitions" { 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, "disabled") + 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) + service = optional(string) + enable_cloudwatch_logging = optional(bool) + create_cloudwatch_log_group = optional(bool) cloudwatch_log_group_name = optional(string) - cloudwatch_log_group_use_name_prefix = optional(bool, false) + cloudwatch_log_group_use_name_prefix = optional(bool) cloudwatch_log_group_class = optional(string) - cloudwatch_log_group_retention_in_days = optional(number, 14) + cloudwatch_log_group_retention_in_days = optional(number) cloudwatch_log_group_kms_key_id = optional(string) })) default = {} @@ -633,6 +645,7 @@ variable "network_mode" { description = "Docker networking mode to use for the containers in the task. Valid values are `none`, `bridge`, `awsvpc`, and `host`" type = string default = "awsvpc" + nullable = false } variable "pid_mode" { @@ -655,6 +668,7 @@ variable "requires_compatibilities" { description = "Set of launch types required by the task. The valid values are `EC2` and `FARGATE`" type = list(string) default = ["FARGATE"] + nullable = false } variable "runtime_platform" { @@ -667,6 +681,7 @@ variable "runtime_platform" { operating_system_family = "LINUX" cpu_architecture = "X86_64" } + nullable = false } variable "skip_destroy" { @@ -688,6 +703,7 @@ variable "track_latest" { description = "Whether should track latest `ACTIVE` task definition on AWS or the one created with the resource stored in state. Useful in the event the task definition is modified outside of this resource" type = bool default = true + nullable = false } variable "volume" { @@ -729,6 +745,7 @@ variable "task_tags" { description = "A map of additional tags to add to the task definition/set created" type = map(string) default = {} + nullable = false } ################################################################################ @@ -740,6 +757,7 @@ variable "create_task_exec_iam_role" { description = "Determines whether the ECS task definition IAM role should be created" type = bool default = true + nullable = false } variable "task_exec_iam_role_arn" { @@ -758,6 +776,7 @@ variable "task_exec_iam_role_use_name_prefix" { description = "Determines whether the IAM role name (`task_exec_iam_role_name`) is used as a prefix" type = bool default = true + nullable = false } variable "task_exec_iam_role_path" { @@ -782,12 +801,14 @@ variable "task_exec_iam_role_tags" { description = "A map of additional tags to add to the IAM role created" type = map(string) default = {} + nullable = false } variable "task_exec_iam_role_policies" { description = "Map of IAM role policy ARNs to attach to the IAM role" type = map(string) default = {} + nullable = false } variable "task_exec_iam_role_max_session_duration" { @@ -800,18 +821,21 @@ variable "create_task_exec_policy" { description = "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" type = bool default = true + nullable = false } variable "task_exec_ssm_param_arns" { description = "List of SSM parameter ARNs the task execution role will be permitted to get/read" type = list(string) default = [] + nullable = false } variable "task_exec_secret_arns" { description = "List of SecretsManager secret ARNs the task execution role will be permitted to get/read" type = list(string) default = [] + nullable = false } variable "task_exec_iam_statements" { @@ -855,6 +879,7 @@ variable "create_tasks_iam_role" { description = "Determines whether the ECS tasks IAM role should be created" type = bool default = true + nullable = false } variable "tasks_iam_role_arn" { @@ -873,6 +898,7 @@ variable "tasks_iam_role_use_name_prefix" { description = "Determines whether the IAM role name (`tasks_iam_role_name`) is used as a prefix" type = bool default = true + nullable = false } variable "tasks_iam_role_path" { @@ -897,12 +923,14 @@ variable "tasks_iam_role_tags" { description = "A map of additional tags to add to the IAM role created" type = map(string) default = {} + nullable = false } variable "tasks_iam_role_policies" { description = "Map of additioanl IAM role policy ARNs to attach to the IAM role" type = map(string) default = {} + nullable = false } variable "tasks_iam_role_statements" { @@ -970,18 +998,21 @@ variable "enable_autoscaling" { description = "Determines whether to enable autoscaling for the service" type = bool default = true + nullable = false } variable "autoscaling_min_capacity" { description = "Minimum number of tasks to run in your service" type = number default = 1 + nullable = false } variable "autoscaling_max_capacity" { description = "Maximum number of tasks to run in your service" type = number default = 10 + nullable = false } variable "autoscaling_policies" { @@ -1059,6 +1090,7 @@ variable "autoscaling_policies" { } } } + nullable = false } variable "autoscaling_scheduled_actions" { @@ -1083,6 +1115,7 @@ variable "create_security_group" { description = "Determines if a security group is created" type = bool default = true + nullable = false } variable "security_group_name" { @@ -1095,6 +1128,7 @@ variable "security_group_use_name_prefix" { description = "Determines whether the security group name (`security_group_name`) is used as a prefix" type = bool default = true + nullable = false } variable "security_group_description" { @@ -1118,7 +1152,8 @@ variable "security_group_ingress_rules" { tags = optional(map(string), {}) to_port = optional(string) })) - default = {} + default = {} + nullable = false } variable "security_group_egress_rules" { @@ -1136,13 +1171,15 @@ variable "security_group_egress_rules" { tags = optional(map(string), {}) to_port = optional(string) })) - default = {} + default = {} + nullable = false } variable "security_group_tags" { description = "A map of additional tags to add to the security group created" type = map(string) default = {} + nullable = false } ############################################################################################ @@ -1153,6 +1190,7 @@ variable "create_infrastructure_iam_role" { description = "Determines whether the ECS infrastructure IAM role should be created" type = bool default = true + nullable = false } variable "infrastructure_iam_role_arn" { @@ -1171,6 +1209,7 @@ variable "infrastructure_iam_role_use_name_prefix" { description = "Determines whether the IAM role name (`iam_role_name`) is used as a prefix" type = bool default = true + nullable = false } variable "infrastructure_iam_role_path" { @@ -1195,4 +1234,5 @@ variable "infrastructure_iam_role_tags" { description = "A map of additional tags to add to the IAM role created" type = map(string) default = {} + nullable = false } diff --git a/variables.tf b/variables.tf index 47b5928..9aba2f2 100644 --- a/variables.tf +++ b/variables.tf @@ -259,16 +259,16 @@ variable "task_exec_iam_statements" { variable "services" { description = "Map of service definitions to create" type = map(object({ - create = optional(bool, true) - create_service = optional(bool, true) - tags = optional(map(string), {}) + create = optional(bool) + create_service = optional(bool) + tags = optional(map(string)) # Service - ignore_task_definition_changes = optional(bool, false) + ignore_task_definition_changes = optional(bool) alarms = optional(object({ alarm_names = list(string) - enable = optional(bool, true) - rollback = optional(bool, true) + enable = optional(bool) + rollback = optional(bool) })) availability_zone_rebalancing = optional(string) capacity_provider_strategy = optional(map(object({ @@ -295,12 +295,12 @@ variable "services" { 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) + enable_ecs_managed_tags = optional(bool) + enable_execute_command = optional(bool) force_delete = optional(bool) - force_new_deployment = optional(bool, true) + force_new_deployment = optional(bool) health_check_grace_period_seconds = optional(number) - launch_type = optional(string, "FARGATE") + launch_type = optional(string) load_balancer = optional(map(object({ container_name = string container_port = number @@ -314,9 +314,9 @@ variable "services" { })) }))) 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), []) + assign_public_ip = optional(bool) + security_group_ids = optional(list(string)) + subnet_ids = optional(list(string)) ordered_placement_strategy = optional(map(object({ field = optional(string) type = string @@ -329,7 +329,7 @@ variable "services" { propagate_tags = optional(string) scheduling_strategy = optional(string) service_connect_configuration = optional(object({ - enabled = optional(bool, true) + enabled = optional(bool) log_configuration = optional(object({ log_driver = string options = optional(map(string)) @@ -390,7 +390,7 @@ variable "services" { size_in_gb = optional(number) snapshot_id = optional(string) tag_specifications = optional(list(object({ - propagate_tags = optional(string, "TASK_DEFINITION") + propagate_tags = optional(string) resource_type = string tags = optional(map(string)) }))) @@ -404,16 +404,16 @@ variable "services" { port_name = string })) wait_for_steady_state = optional(bool) - service_tags = optional(map(string), {}) + service_tags = optional(map(string)) # Service - IAM Role - create_iam_role = optional(bool, true) + create_iam_role = optional(bool) iam_role_arn = optional(string) iam_role_name = optional(string) - iam_role_use_name_prefix = optional(bool, true) + iam_role_use_name_prefix = optional(bool) iam_role_path = optional(string) iam_role_description = optional(string) iam_role_permissions_boundary = optional(string) - iam_role_tags = optional(map(string), {}) + iam_role_tags = optional(map(string)) iam_role_statements = optional(list(object({ sid = optional(string) actions = optional(list(string)) @@ -436,7 +436,7 @@ variable "services" { }))) }))) # Task Definition - create_task_definition = optional(bool, true) + create_task_definition = optional(bool) task_definition_arn = optional(string) container_definitions = optional(map(object({ operating_system_family = optional(string) @@ -568,10 +568,10 @@ variable "services" { # Cloudwatch Log Group service = optional(string, "") - enable_cloudwatch_logging = optional(bool, true) - create_cloudwatch_log_group = optional(bool, true) + enable_cloudwatch_logging = optional(bool) + create_cloudwatch_log_group = optional(bool) cloudwatch_log_group_name = optional(string) - cloudwatch_log_group_use_name_prefix = optional(bool, false) + cloudwatch_log_group_use_name_prefix = optional(bool) cloudwatch_log_group_class = optional(string) cloudwatch_log_group_retention_in_days = optional(number) cloudwatch_log_group_kms_key_id = optional(string) @@ -584,30 +584,24 @@ variable "services" { family = optional(string) ipc_mode = optional(string) memory = optional(number, 2048) - network_mode = optional(string, "awsvpc") + network_mode = optional(string) pid_mode = optional(string) proxy_configuration = optional(object({ container_name = string properties = optional(map(string)) type = optional(string) })) - requires_compatibilities = optional(list(string), ["FARGATE"]) + requires_compatibilities = optional(list(string)) 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" - } - ) + cpu_architecture = optional(string) + operating_system_family = optional(string) + })) skip_destroy = optional(bool) task_definition_placement_constraints = optional(map(object({ expression = optional(string) type = string }))) - track_latest = optional(bool, true) + track_latest = optional(bool) volume = optional(map(object({ configure_at_launch = optional(bool) docker_volume_configuration = optional(object({ @@ -638,21 +632,21 @@ variable "services" { host_path = optional(string) name = optional(string) }))) - task_tags = optional(map(string), {}) + task_tags = optional(map(string)) # Task Execution - IAM Role - create_task_exec_iam_role = optional(bool, true) + create_task_exec_iam_role = optional(bool) 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_use_name_prefix = optional(bool) 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_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), []) + create_task_exec_policy = optional(bool) + 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)) @@ -676,15 +670,15 @@ variable "services" { }))) task_exec_iam_policy_path = optional(string) # Tasks - IAM Role - create_tasks_iam_role = optional(bool, true) + create_tasks_iam_role = optional(bool) tasks_iam_role_arn = optional(string) tasks_iam_role_name = optional(string) - tasks_iam_role_use_name_prefix = optional(bool, true) + tasks_iam_role_use_name_prefix = optional(bool) 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_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)) @@ -715,12 +709,12 @@ variable "services" { 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) + enable_autoscaling = optional(bool) + autoscaling_min_capacity = optional(number) + autoscaling_max_capacity = optional(number) autoscaling_policies = optional(map(object({ name = optional(string) # Will fall back to the key name if not provided - policy_type = optional(string, "TargetTrackingScaling") + policy_type = optional(string) step_scaling_policy_configuration = optional(object({ adjustment_type = optional(string) cooldown = optional(number) @@ -767,33 +761,11 @@ variable "services" { 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) + scale_in_cooldown = optional(number) + scale_out_cooldown = optional(number) + target_value = optional(number) })) - })), - # 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 @@ -804,48 +776,42 @@ variable "services" { timezone = optional(string) }))) # Security Group - create_security_group = optional(bool, true) + create_security_group = optional(bool) security_group_name = optional(string) - security_group_use_name_prefix = optional(bool, true) + security_group_use_name_prefix = optional(bool) 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") + ip_protocol = optional(string) prefix_list_id = optional(string) referenced_security_group_id = optional(string) - tags = optional(map(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") + ip_protocol = optional(string) prefix_list_id = optional(string) referenced_security_group_id = optional(string) - tags = optional(map(string), {}) + tags = optional(map(string)) to_port = optional(string) - })), - # Default - {} - ) - security_group_tags = optional(map(string), {}) + }))) + security_group_tags = optional(map(string)) # ECS Infrastructure IAM Role - create_infrastructure_iam_role = optional(bool, true) + create_infrastructure_iam_role = optional(bool) infrastructure_iam_role_arn = optional(string) infrastructure_iam_role_name = optional(string) - infrastructure_iam_role_use_name_prefix = optional(bool, true) + infrastructure_iam_role_use_name_prefix = optional(bool) 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), {}) + infrastructure_iam_role_tags = optional(map(string)) })) default = null } diff --git a/wrappers/container-definition/main.tf b/wrappers/container-definition/main.tf index 3dc15a8..16b8919 100644 --- a/wrappers/container-definition/main.tf +++ b/wrappers/container-definition/main.tf @@ -30,22 +30,20 @@ module "wrapper" { image = try(each.value.image, var.defaults.image, null) interactive = try(each.value.interactive, var.defaults.interactive, false) links = try(each.value.links, var.defaults.links, null) - linuxParameters = try(each.value.linuxParameters, var.defaults.linuxParameters, { - initProcessEnabled = false - }) - logConfiguration = try(each.value.logConfiguration, var.defaults.logConfiguration, {}) - memory = try(each.value.memory, var.defaults.memory, null) - memoryReservation = try(each.value.memoryReservation, var.defaults.memoryReservation, null) - mountPoints = try(each.value.mountPoints, var.defaults.mountPoints, []) - name = try(each.value.name, var.defaults.name, null) - operating_system_family = try(each.value.operating_system_family, var.defaults.operating_system_family, "LINUX") - portMappings = try(each.value.portMappings, var.defaults.portMappings, null) - privileged = try(each.value.privileged, var.defaults.privileged, false) - pseudoTerminal = try(each.value.pseudoTerminal, var.defaults.pseudoTerminal, false) - readonlyRootFilesystem = try(each.value.readonlyRootFilesystem, var.defaults.readonlyRootFilesystem, true) - region = try(each.value.region, var.defaults.region, null) - repositoryCredentials = try(each.value.repositoryCredentials, var.defaults.repositoryCredentials, null) - resourceRequirements = try(each.value.resourceRequirements, var.defaults.resourceRequirements, null) + linuxParameters = try(each.value.linuxParameters, var.defaults.linuxParameters, {}) + logConfiguration = try(each.value.logConfiguration, var.defaults.logConfiguration, {}) + memory = try(each.value.memory, var.defaults.memory, null) + memoryReservation = try(each.value.memoryReservation, var.defaults.memoryReservation, null) + mountPoints = try(each.value.mountPoints, var.defaults.mountPoints, []) + name = try(each.value.name, var.defaults.name, null) + operating_system_family = try(each.value.operating_system_family, var.defaults.operating_system_family, "LINUX") + portMappings = try(each.value.portMappings, var.defaults.portMappings, null) + privileged = try(each.value.privileged, var.defaults.privileged, false) + pseudoTerminal = try(each.value.pseudoTerminal, var.defaults.pseudoTerminal, false) + readonlyRootFilesystem = try(each.value.readonlyRootFilesystem, var.defaults.readonlyRootFilesystem, true) + region = try(each.value.region, var.defaults.region, null) + repositoryCredentials = try(each.value.repositoryCredentials, var.defaults.repositoryCredentials, null) + resourceRequirements = try(each.value.resourceRequirements, var.defaults.resourceRequirements, null) restartPolicy = try(each.value.restartPolicy, var.defaults.restartPolicy, { enabled = true }) From 0212fa65c0e3177aea396a75be4fe59a0605f7af Mon Sep 17 00:00:00 2001 From: Bryant Biggs Date: Wed, 6 Aug 2025 10:51:02 -0500 Subject: [PATCH 2/3] chore: Correct comment --- examples/container-definition/outputs.tf | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/examples/container-definition/outputs.tf b/examples/container-definition/outputs.tf index 18eae3f..d085c9d 100644 --- a/examples/container-definition/outputs.tf +++ b/examples/container-definition/outputs.tf @@ -12,14 +12,13 @@ output "container_definition_json" { value = module.ecs_container_definition.container_definition_json } -# Need the output pretty-printed and sorted for comparison resource "null_resource" "container_definition_json" { triggers = { container_definition_json = timestamp() } provisioner "local-exec" { - # Bootstrap script called with private_ip of each node in the cluster + # Need the output pretty-printed and sorted for comparison command = "echo '${module.ecs_container_definition.container_definition_json}' | jq -S > ./definition.json" } } From 2354bb2a1a04b182d450d0736cb20cd5a5b0eedd Mon Sep 17 00:00:00 2001 From: Bryant Biggs Date: Thu, 7 Aug 2025 08:50:31 -0500 Subject: [PATCH 3/3] chore: Add variable to enable/disable writing container definition to file --- examples/container-definition/README.md | 4 +++- examples/container-definition/outputs.tf | 2 ++ examples/container-definition/variables.tf | 5 +++++ 3 files changed, 10 insertions(+), 1 deletion(-) diff --git a/examples/container-definition/README.md b/examples/container-definition/README.md index 0da617a..0a65ec6 100644 --- a/examples/container-definition/README.md +++ b/examples/container-definition/README.md @@ -45,7 +45,9 @@ Note that this example may create resources which will incur monetary charges on ## Inputs -No inputs. +| Name | Description | Type | Default | Required | +|------|-------------|------|---------|:--------:| +| [write\_container\_definition\_to\_file](#input\_write\_container\_definition\_to\_file) | Determines whether the container definition JSON should be written to a file. Used for debugging and checking diffs | `bool` | `true` | no | ## Outputs diff --git a/examples/container-definition/outputs.tf b/examples/container-definition/outputs.tf index d085c9d..16521cf 100644 --- a/examples/container-definition/outputs.tf +++ b/examples/container-definition/outputs.tf @@ -13,6 +13,8 @@ output "container_definition_json" { } resource "null_resource" "container_definition_json" { + count = var.write_container_definition_to_file ? 1 : 0 + triggers = { container_definition_json = timestamp() } diff --git a/examples/container-definition/variables.tf b/examples/container-definition/variables.tf index e69de29..41b8ab9 100644 --- a/examples/container-definition/variables.tf +++ b/examples/container-definition/variables.tf @@ -0,0 +1,5 @@ +variable "write_container_definition_to_file" { + description = "Determines whether the container definition JSON should be written to a file. Used for debugging and checking diffs" + type = bool + default = true +}