diff --git a/README.md b/README.md index 48487cf6..2900c4c5 100644 --- a/README.md +++ b/README.md @@ -34,6 +34,7 @@ module "ecs" { } # Cluster capacity providers + cluster_capacity_providers = ["FARGATE", "FARGATE_SPOT"] default_capacity_provider_strategy = { FARGATE = { weight = 50 @@ -146,9 +147,11 @@ module "ecs" { ## Examples -- [ECS Cluster Complete](https://github.com/terraform-aws-modules/terraform-aws-ecs/tree/master/examples/complete) -- [ECS Cluster w/ EC2 Autoscaling Capacity Provider](https://github.com/terraform-aws-modules/terraform-aws-ecs/tree/master/examples/ec2-autoscaling) -- [ECS Cluster w/ Fargate Capacity Provider](https://github.com/terraform-aws-modules/terraform-aws-ecs/tree/master/examples/fargate) +- [ECS cluster w/ integrated service(s)](https://github.com/terraform-aws-modules/terraform-aws-ecs/tree/master/examples/complete) +- [ECS container definition](https://github.com/terraform-aws-modules/terraform-aws-ecs/tree/master/examples/container-definition) +- [ECS cluster w/ EC2 Autoscaling capacity provider](https://github.com/terraform-aws-modules/terraform-aws-ecs/tree/master/examples/ec2-autoscaling) +- [ECS cluster w/ Fargate capacity provider](https://github.com/terraform-aws-modules/terraform-aws-ecs/tree/master/examples/fargate) +- [ECS cluster w/ ECS managed instances capacity provider](https://github.com/terraform-aws-modules/terraform-aws-ecs/tree/master/examples/managed-instances) ## Requirements @@ -156,7 +159,7 @@ module "ecs" { | Name | Version | |------|---------| | [terraform](#requirement\_terraform) | >= 1.5.7 | -| [aws](#requirement\_aws) | >= 6.21 | +| [aws](#requirement\_aws) | >= 6.23 | ## Providers @@ -177,12 +180,13 @@ No resources. | Name | Description | Type | Default | Required | |------|-------------|------|---------|:--------:| -| [autoscaling\_capacity\_providers](#input\_autoscaling\_capacity\_providers) | Map of autoscaling capacity provider definitions to create for the cluster |
map(object({
auto_scaling_group_arn = string
managed_draining = optional(string, "ENABLED")
managed_scaling = optional(object({
instance_warmup_period = optional(number)
maximum_scaling_step_size = optional(number)
minimum_scaling_step_size = optional(number)
status = optional(string)
target_capacity = optional(number)
}))
managed_termination_protection = optional(string)
name = optional(string) # Will fall back to use map key if not set
tags = optional(map(string), {})
})) | `null` | no |
+| [capacity\_providers](#input\_capacity\_providers) | Map of capacity provider definitions to create for the cluster | map(object({
auto_scaling_group_provider = optional(object({
auto_scaling_group_arn = string
managed_draining = optional(string, "ENABLED")
managed_scaling = optional(object({
instance_warmup_period = optional(number)
maximum_scaling_step_size = optional(number)
minimum_scaling_step_size = optional(number)
status = optional(string)
target_capacity = optional(number)
}))
managed_termination_protection = optional(string)
}))
managed_instances_provider = optional(object({
infrastructure_role_arn = optional(string)
instance_launch_template = object({
ec2_instance_profile_arn = optional(string)
instance_requirements = optional(object({
accelerator_count = optional(object({
max = optional(number)
min = optional(number)
}))
accelerator_manufacturers = optional(list(string))
accelerator_names = optional(list(string))
accelerator_total_memory_mib = optional(object({
max = optional(number)
min = optional(number)
}))
accelerator_types = optional(list(string))
allowed_instance_types = optional(list(string))
bare_metal = optional(string)
baseline_ebs_bandwidth_mbps = optional(object({
max = optional(number)
min = optional(number)
}))
burstable_performance = optional(string)
cpu_manufacturers = optional(list(string))
excluded_instance_types = optional(list(string))
instance_generations = optional(list(string))
local_storage = optional(string)
local_storage_types = optional(list(string))
max_spot_price_as_percentage_of_optimal_on_demand_price = optional(number)
memory_gib_per_vcpu = optional(object({
max = optional(number)
min = optional(number)
}))
memory_mib = optional(object({
max = optional(number)
min = optional(number)
}))
network_bandwidth_gbps = optional(object({
max = optional(number)
min = optional(number)
}))
network_interface_count = optional(object({
max = optional(number)
min = optional(number)
}))
on_demand_max_price_percentage_over_lowest_price = optional(number)
require_hibernate_support = optional(bool)
spot_max_price_percentage_over_lowest_price = optional(number)
total_local_storage_gb = optional(object({
max = optional(number)
min = optional(number)
}))
vcpu_count = optional(object({
max = optional(number)
min = optional(number)
}))
}))
monitoring = optional(string)
network_configuration = optional(object({
security_groups = optional(list(string), [])
subnets = list(string)
}))
storage_configuration = optional(object({
storage_size_gib = number
}))
})
propagate_tags = optional(string, "CAPACITY_PROVIDER")
}))
name = optional(string) # Will fall back to use map key if not set
tags = optional(map(string), {})
})) | `null` | no |
| [cloudwatch\_log\_group\_class](#input\_cloudwatch\_log\_group\_class) | Specified the log class of the log group. Possible values are: `STANDARD` or `INFREQUENT_ACCESS` | `string` | `null` | no |
| [cloudwatch\_log\_group\_kms\_key\_id](#input\_cloudwatch\_log\_group\_kms\_key\_id) | If a KMS Key ARN is set, this key will be used to encrypt the corresponding log group. Please be sure that the KMS Key has an appropriate key policy (https://docs.aws.amazon.com/AmazonCloudWatch/latest/logs/encrypt-log-data-kms.html) | `string` | `null` | no |
| [cloudwatch\_log\_group\_name](#input\_cloudwatch\_log\_group\_name) | Custom name of CloudWatch Log Group for ECS cluster | `string` | `null` | no |
| [cloudwatch\_log\_group\_retention\_in\_days](#input\_cloudwatch\_log\_group\_retention\_in\_days) | Number of days to retain log events | `number` | `90` | no |
| [cloudwatch\_log\_group\_tags](#input\_cloudwatch\_log\_group\_tags) | A map of additional tags to add to the log group created | `map(string)` | `{}` | no |
+| [cluster\_capacity\_providers](#input\_cluster\_capacity\_providers) | List of capacity provider names to associate with the ECS cluster. Note: any capacity providers created by this module will be automatically added | `list(string)` | `[]` | no |
| [cluster\_configuration](#input\_cluster\_configuration) | The execute command configuration for the cluster | object({
execute_command_configuration = optional(object({
kms_key_id = optional(string)
log_configuration = optional(object({
cloud_watch_encryption_enabled = optional(bool)
cloud_watch_log_group_name = optional(string)
s3_bucket_encryption_enabled = optional(bool)
s3_bucket_name = optional(string)
s3_kms_key_id = optional(string)
s3_key_prefix = optional(string)
}))
logging = optional(string, "OVERRIDE")
}))
managed_storage_configuration = optional(object({
fargate_ephemeral_storage_kms_key_id = optional(string)
kms_key_id = optional(string)
}))
}) | {
"execute_command_configuration": {
"log_configuration": {
"cloud_watch_log_group_name": "placeholder"
}
}
} | no |
| [cluster\_name](#input\_cluster\_name) | Name of the cluster (up to 255 letters, numbers, hyphens, and underscores) | `string` | `""` | no |
| [cluster\_service\_connect\_defaults](#input\_cluster\_service\_connect\_defaults) | Configures a default Service Connect namespace | object({
namespace = string
}) | `null` | no |
@@ -190,11 +194,40 @@ No resources.
| [cluster\_tags](#input\_cluster\_tags) | A map of additional tags to add to the cluster | `map(string)` | `{}` | no |
| [create](#input\_create) | Determines whether resources will be created (affects all resources) | `bool` | `true` | no |
| [create\_cloudwatch\_log\_group](#input\_create\_cloudwatch\_log\_group) | Determines whether a log group is created by this module for the cluster logs. If not, AWS will automatically create one if logging is enabled | `bool` | `true` | no |
+| [create\_infrastructure\_iam\_role](#input\_create\_infrastructure\_iam\_role) | Determines whether the ECS infrastructure IAM role should be created | `bool` | `true` | no |
+| [create\_node\_iam\_instance\_profile](#input\_create\_node\_iam\_instance\_profile) | Determines whether an IAM instance profile is created or to use an existing IAM instance profile | `bool` | `true` | no |
+| [create\_security\_group](#input\_create\_security\_group) | Determines if a security group is created | `bool` | `true` | no |
| [create\_task\_exec\_iam\_role](#input\_create\_task\_exec\_iam\_role) | Determines whether the ECS task definition IAM role should be created | `bool` | `false` | no |
| [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 |
+| [disable\_v7\_default\_name\_description](#input\_disable\_v7\_default\_name\_description) | [DEPRECATED - will be removed in v9.0] Determines whether to disable the default postfix added to resource names and descriptions added in v7.0 | `bool` | `false` | no |
+| [infrastructure\_iam\_role\_description](#input\_infrastructure\_iam\_role\_description) | Description of the role | `string` | `null` | no |
+| [infrastructure\_iam\_role\_name](#input\_infrastructure\_iam\_role\_name) | Name to use on IAM role created | `string` | `null` | no |
+| [infrastructure\_iam\_role\_override\_policy\_documents](#input\_infrastructure\_iam\_role\_override\_policy\_documents) | List of IAM policy documents that are merged together into the exported document. In merging, statements with non-blank `sid`s will override statements with the same `sid` | `list(string)` | `[]` | no |
+| [infrastructure\_iam\_role\_path](#input\_infrastructure\_iam\_role\_path) | IAM role path | `string` | `null` | no |
+| [infrastructure\_iam\_role\_permissions\_boundary](#input\_infrastructure\_iam\_role\_permissions\_boundary) | ARN of the policy that is used to set the permissions boundary for the IAM role | `string` | `null` | no |
+| [infrastructure\_iam\_role\_source\_policy\_documents](#input\_infrastructure\_iam\_role\_source\_policy\_documents) | List of IAM policy documents that are merged together into the exported document. Statements must have unique `sid`s | `list(string)` | `[]` | no |
+| [infrastructure\_iam\_role\_statements](#input\_infrastructure\_iam\_role\_statements) | A map of IAM policy [statements](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document#statement) for custom permission usage | map(object({
sid = optional(string)
actions = optional(list(string))
not_actions = optional(list(string))
effect = optional(string, "Allow")
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
variable = string
values = list(string)
})))
})) | `null` | no |
+| [infrastructure\_iam\_role\_tags](#input\_infrastructure\_iam\_role\_tags) | A map of additional tags to add to the IAM role created | `map(string)` | `{}` | no |
+| [infrastructure\_iam\_role\_use\_name\_prefix](#input\_infrastructure\_iam\_role\_use\_name\_prefix) | Determines whether the IAM role name (`iam_role_name`) is used as a prefix | `bool` | `true` | no |
+| [node\_iam\_role\_additional\_policies](#input\_node\_iam\_role\_additional\_policies) | Additional policies to be added to the IAM role | `map(string)` | `{}` | no |
+| [node\_iam\_role\_description](#input\_node\_iam\_role\_description) | Description of the role | `string` | `"ECS Managed Instances node IAM role"` | no |
+| [node\_iam\_role\_name](#input\_node\_iam\_role\_name) | Name to use on IAM role/instance profile created | `string` | `null` | no |
+| [node\_iam\_role\_override\_policy\_documents](#input\_node\_iam\_role\_override\_policy\_documents) | List of IAM policy documents that are merged together into the exported document. In merging, statements with non-blank `sid`s will override statements with the same `sid` | `list(string)` | `[]` | no |
+| [node\_iam\_role\_path](#input\_node\_iam\_role\_path) | IAM role/instance profile path | `string` | `null` | no |
+| [node\_iam\_role\_permissions\_boundary](#input\_node\_iam\_role\_permissions\_boundary) | ARN of the policy that is used to set the permissions boundary for the IAM role | `string` | `null` | no |
+| [node\_iam\_role\_source\_policy\_documents](#input\_node\_iam\_role\_source\_policy\_documents) | List of IAM policy documents that are merged together into the exported document. Statements must have unique `sid`s | `list(string)` | `[]` | no |
+| [node\_iam\_role\_statements](#input\_node\_iam\_role\_statements) | A map of IAM policy [statements](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document#statement) for custom permission usage | map(object({
sid = optional(string)
actions = optional(list(string))
not_actions = optional(list(string))
effect = optional(string, "Allow")
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
variable = string
values = list(string)
})))
})) | `null` | no |
+| [node\_iam\_role\_tags](#input\_node\_iam\_role\_tags) | A map of additional tags to add to the IAM role/instance profile created | `map(string)` | `{}` | no |
+| [node\_iam\_role\_use\_name\_prefix](#input\_node\_iam\_role\_use\_name\_prefix) | Determines whether the IAM role/instance profile name (`node_iam_role_name`) is used as a prefix | `bool` | `true` | 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)
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)
canary_configuration = optional(object({
canary_bake_time_in_minutes = optional(string)
canary_percent = optional(string)
}))
linear_configuration = optional(object({
step_bake_time_in_minutes = optional(string)
step_percent = optional(string)
}))
lifecycle_hook = optional(map(object({
hook_target_arn = string
role_arn = string
lifecycle_stages = list(string)
hook_details = optional(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
}))
sigint_rollback = optional(bool)
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)
credentialSpecs = optional(list(string))
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)
predictive_scaling_policy_configuration = optional(object({
max_capacity_breach_behavior = optional(string)
max_capacity_buffer = optional(number)
metric_specification = list(object({
customized_capacity_metric_specification = optional(object({
metric_data_query = list(object({
expression = optional(string)
id = string
label = optional(string)
metric_stat = optional(object({
metric = object({
dimension = optional(list(object({
name = string
value = string
})))
metric_name = optional(string)
namespace = optional(string)
})
stat = string
unit = optional(string)
}))
return_data = optional(bool)
}))
}))
customized_load_metric_specification = optional(object({
metric_data_query = list(object({
expression = optional(string)
id = string
label = optional(string)
metric_stat = optional(object({
metric = object({
dimension = optional(list(object({
name = string
value = string
})))
metric_name = optional(string)
namespace = optional(string)
})
stat = string
unit = optional(string)
}))
return_data = optional(bool)
}))
}))
customized_scaling_metric_specification = optional(object({
metric_data_query = list(object({
expression = optional(string)
id = string
label = optional(string)
metric_stat = optional(object({
metric = object({
dimension = optional(list(object({
name = string
value = string
})))
metric_name = optional(string)
namespace = optional(string)
})
stat = string
unit = optional(string)
}))
return_data = optional(bool)
}))
}))
predefined_load_metric_specification = optional(object({
predefined_metric_type = string
resource_label = optional(string)
}))
predefined_metric_pair_specification = optional(object({
predefined_metric_type = string
resource_label = optional(string)
}))
predefined_scaling_metric_specification = optional(object({
predefined_metric_type = string
resource_label = optional(string)
}))
target_value = number
}))
mode = optional(string)
scheduling_buffer_time = optional(number)
}))
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)
})))
autoscaling_suspended_state = optional(object({
dynamic_scaling_in_suspended = optional(bool)
dynamic_scaling_out_suspended = optional(bool)
scheduled_scaling_suspended = optional(bool)
}))
# Security Group
create_security_group = optional(bool)
vpc_id = optional(string)
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 |
+| [security\_group\_description](#input\_security\_group\_description) | Description of the security group created | `string` | `null` | no |
+| [security\_group\_egress\_rules](#input\_security\_group\_egress\_rules) | Security group egress rules to add to the security group created | map(object({
name = optional(string)
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)
})) | {
"all_ipv4": {
"cidr_ipv4": "0.0.0.0/0",
"description": "Allow all IPv4 traffic",
"ip_protocol": "-1"
},
"all_ipv6": {
"cidr_ipv6": "::/0",
"description": "Allow all IPv6 traffic",
"ip_protocol": "-1"
}
} | no |
+| [security\_group\_ingress\_rules](#input\_security\_group\_ingress\_rules) | Security group ingress rules to add to the security group created | map(object({
name = optional(string)
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)
})) | `{}` | no |
+| [security\_group\_name](#input\_security\_group\_name) | Name to use on security group created | `string` | `null` | no |
+| [security\_group\_tags](#input\_security\_group\_tags) | A map of additional tags to add to the security group created | `map(string)` | `{}` | no |
+| [security\_group\_use\_name\_prefix](#input\_security\_group\_use\_name\_prefix) | Determines whether the security group name (`security_group_name`) is used as a prefix | `bool` | `true` | no |
+| [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)
canary_configuration = optional(object({
canary_bake_time_in_minutes = optional(string)
canary_percent = optional(string)
}))
linear_configuration = optional(object({
step_bake_time_in_minutes = optional(string)
step_percent = optional(string)
}))
lifecycle_hook = optional(map(object({
hook_target_arn = string
role_arn = optional(string)
lifecycle_stages = list(string)
hook_details = optional(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 # Should be optional but bug in provider
role_arn = optional(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(list(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
}))
sigint_rollback = optional(bool)
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)
credentialSpecs = optional(list(string))
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)
predictive_scaling_policy_configuration = optional(object({
max_capacity_breach_behavior = optional(string)
max_capacity_buffer = optional(number)
metric_specification = list(object({
customized_capacity_metric_specification = optional(object({
metric_data_query = list(object({
expression = optional(string)
id = string
label = optional(string)
metric_stat = optional(object({
metric = object({
dimension = optional(list(object({
name = string
value = string
})))
metric_name = optional(string)
namespace = optional(string)
})
stat = string
unit = optional(string)
}))
return_data = optional(bool)
}))
}))
customized_load_metric_specification = optional(object({
metric_data_query = list(object({
expression = optional(string)
id = string
label = optional(string)
metric_stat = optional(object({
metric = object({
dimension = optional(list(object({
name = string
value = string
})))
metric_name = optional(string)
namespace = optional(string)
})
stat = string
unit = optional(string)
}))
return_data = optional(bool)
}))
}))
customized_scaling_metric_specification = optional(object({
metric_data_query = list(object({
expression = optional(string)
id = string
label = optional(string)
metric_stat = optional(object({
metric = object({
dimension = optional(list(object({
name = string
value = string
})))
metric_name = optional(string)
namespace = optional(string)
})
stat = string
unit = optional(string)
}))
return_data = optional(bool)
}))
}))
predefined_load_metric_specification = optional(object({
predefined_metric_type = string
resource_label = optional(string)
}))
predefined_metric_pair_specification = optional(object({
predefined_metric_type = string
resource_label = optional(string)
}))
predefined_scaling_metric_specification = optional(object({
predefined_metric_type = string
resource_label = optional(string)
}))
target_value = number
}))
mode = optional(string)
scheduling_buffer_time = optional(number)
}))
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)
})))
autoscaling_suspended_state = optional(object({
dynamic_scaling_in_suspended = optional(bool)
dynamic_scaling_out_suspended = optional(bool)
scheduled_scaling_suspended = optional(bool)
}))
# Security Group
create_security_group = optional(bool)
vpc_id = optional(string)
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 |
@@ -206,18 +239,28 @@ No resources.
| [task\_exec\_iam\_statements](#input\_task\_exec\_iam\_statements) | A map of IAM policy [statements](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document#statement) for custom permission usage | map(object({
sid = optional(string)
actions = optional(list(string))
not_actions = optional(list(string))
effect = optional(string, "Allow")
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
variable = string
values = list(string)
})))
})) | `null` | no |
| [task\_exec\_secret\_arns](#input\_task\_exec\_secret\_arns) | List of SecretsManager secret ARNs the task execution role will be permitted to get/read | `list(string)` | `[]` | no |
| [task\_exec\_ssm\_param\_arns](#input\_task\_exec\_ssm\_param\_arns) | List of SSM parameter ARNs the task execution role will be permitted to get/read | `list(string)` | `[]` | no |
+| [vpc\_id](#input\_vpc\_id) | The ID of the VPC where the security group will be created | `string` | `null` | no |
## Outputs
| Name | Description |
|------|-------------|
-| [autoscaling\_capacity\_providers](#output\_autoscaling\_capacity\_providers) | Map of autoscaling capacity providers created and their attributes |
+| [capacity\_providers](#output\_capacity\_providers) | Map of autoscaling capacity providers created and their attributes |
| [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 |
| [cluster\_arn](#output\_cluster\_arn) | ARN that identifies the cluster |
| [cluster\_capacity\_providers](#output\_cluster\_capacity\_providers) | Map of cluster capacity providers attributes |
| [cluster\_id](#output\_cluster\_id) | ID that identifies the cluster |
| [cluster\_name](#output\_cluster\_name) | Name that identifies the cluster |
+| [infrastructure\_iam\_role\_arn](#output\_infrastructure\_iam\_role\_arn) | The Amazon Resource Name (ARN) specifying the IAM role |
+| [infrastructure\_iam\_role\_name](#output\_infrastructure\_iam\_role\_name) | IAM role name |
+| [infrastructure\_iam\_role\_unique\_id](#output\_infrastructure\_iam\_role\_unique\_id) | Stable and unique string identifying the IAM role |
+| [node\_iam\_instance\_profile\_arn](#output\_node\_iam\_instance\_profile\_arn) | ARN assigned by AWS to the instance profile |
+| [node\_iam\_instance\_profile\_id](#output\_node\_iam\_instance\_profile\_id) | Instance profile's ID |
+| [node\_iam\_instance\_profile\_unique](#output\_node\_iam\_instance\_profile\_unique) | Stable and unique string identifying the IAM instance profile |
+| [node\_iam\_role\_arn](#output\_node\_iam\_role\_arn) | The Amazon Resource Name (ARN) specifying the IAM role |
+| [node\_iam\_role\_name](#output\_node\_iam\_role\_name) | IAM role name |
+| [node\_iam\_role\_unique\_id](#output\_node\_iam\_role\_unique\_id) | Stable and unique string identifying the IAM role |
| [services](#output\_services) | Map of services created and their attributes |
| [task\_exec\_iam\_role\_arn](#output\_task\_exec\_iam\_role\_arn) | Task execution IAM role ARN |
| [task\_exec\_iam\_role\_name](#output\_task\_exec\_iam\_role\_name) | Task execution IAM role name |
diff --git a/docs/UPGRADE-7.0.md b/docs/UPGRADE-7.0.md
new file mode 100644
index 00000000..57be806a
--- /dev/null
+++ b/docs/UPGRADE-7.0.md
@@ -0,0 +1,211 @@
+# Upgrade from v6.x to v7.x
+
+If you have any questions regarding this upgrade process, please consult the [`examples`](https://github.com/terraform-aws-modules/terraform-aws-ecs/tree/master/examples) directory:
+If you find a bug, please open an issue with supporting configuration to reproduce.
+
+## List of backwards incompatible changes
+
+- Previously the module would infer the capacity providers to use based on those specified in the `default_capacity_provider_strategy` variable as well as any specified in the `autoscaling_capacity_providers` variable. As of v7.0.0, the module will no longer infer the capacity providers that should be associated with the cluster. Instead, users must explicitly specify the desired capacity providers using the new `cluster_capacity_providers` variable. The only inference of capacity providers are those created by the module itself when using the `capacity_providers` variable. Essentially, if you are using `FARGATE`, `FARGATE_SPOT`, or an externally created capacity provider, you must now specify those in the `cluster_capacity_providers` variable.
+- With the addition of ECS managed instances support, the prior variable `autoscaling_capacity_providers` has been replaced with the more generic `capacity_providers` variable. If you were previously using `autoscaling_capacity_providers`, you will need to migrate to the new `capacity_providers` variable by simply renaming it and nesting each ASG capacity provider definition under the argument `auto_scaling_group_provider`. See the before vs after section below for an example of this change. Note: your existing ASG capacity providers will continue to work as before, this is simply a variable rename and variable definition modification. No resources will be replaced/destroyed as part of this change.
+- The ECS service variable `ordered_placement_strategy` type definition has been changed from `map(object({...}))` to `list(object({...}))`. The argument needs to preserve order so a list is necessary.
+
+## Additional changes
+
+### Added
+
+- Default name postfixes for IAM roles and security groups have been added, along with default descriptions. When using the intended behavior of simply setting a `var.name` value and relying on the module, these new defaults help to distinguish resources created by the module. Instead of seeing 4 IAM roles named `"example-Your application is now running on a container in Amazon ECS using Amazon ECS Managed Instances.
map(object({
auto_scaling_group_arn = string
managed_draining = optional(string, "ENABLED")
managed_scaling = optional(object({
instance_warmup_period = optional(number)
maximum_scaling_step_size = optional(number)
minimum_scaling_step_size = optional(number)
status = optional(string)
target_capacity = optional(number)
}))
managed_termination_protection = optional(string)
name = optional(string) # Will fall back to use map key if not set
tags = optional(map(string), {})
})) | `null` | no |
+| [capacity\_providers](#input\_capacity\_providers) | Map of capacity provider definitions to create | map(object({
auto_scaling_group_provider = optional(object({
auto_scaling_group_arn = string
managed_draining = optional(string, "ENABLED")
managed_scaling = optional(object({
instance_warmup_period = optional(number)
maximum_scaling_step_size = optional(number)
minimum_scaling_step_size = optional(number)
status = optional(string)
target_capacity = optional(number)
}))
managed_termination_protection = optional(string)
}))
managed_instances_provider = optional(object({
infrastructure_role_arn = optional(string)
instance_launch_template = object({
ec2_instance_profile_arn = optional(string)
instance_requirements = optional(object({
accelerator_count = optional(object({
max = optional(number)
min = optional(number)
}))
accelerator_manufacturers = optional(list(string))
accelerator_names = optional(list(string))
accelerator_total_memory_mib = optional(object({
max = optional(number)
min = optional(number)
}))
accelerator_types = optional(list(string))
allowed_instance_types = optional(list(string))
bare_metal = optional(string)
baseline_ebs_bandwidth_mbps = optional(object({
max = optional(number)
min = optional(number)
}))
burstable_performance = optional(string)
cpu_manufacturers = optional(list(string))
excluded_instance_types = optional(list(string))
instance_generations = optional(list(string))
local_storage = optional(string)
local_storage_types = optional(list(string))
max_spot_price_as_percentage_of_optimal_on_demand_price = optional(number)
memory_gib_per_vcpu = optional(object({
max = optional(number)
min = optional(number)
}))
memory_mib = optional(object({
max = optional(number)
min = optional(number)
}))
network_bandwidth_gbps = optional(object({
max = optional(number)
min = optional(number)
}))
network_interface_count = optional(object({
max = optional(number)
min = optional(number)
}))
on_demand_max_price_percentage_over_lowest_price = optional(number)
require_hibernate_support = optional(bool)
spot_max_price_percentage_over_lowest_price = optional(number)
total_local_storage_gb = optional(object({
max = optional(number)
min = optional(number)
}))
vcpu_count = optional(object({
max = optional(number)
min = optional(number)
}))
}))
monitoring = optional(string)
network_configuration = optional(object({
security_groups = optional(list(string), [])
subnets = list(string)
}))
storage_configuration = optional(object({
storage_size_gib = number
}))
})
propagate_tags = optional(string, "CAPACITY_PROVIDER")
}))
name = optional(string) # Will fall back to use map key if not set
tags = optional(map(string), {})
})) | `null` | no |
| [cloudwatch\_log\_group\_class](#input\_cloudwatch\_log\_group\_class) | Specified the log class of the log group. Possible values are: `STANDARD` or `INFREQUENT_ACCESS` | `string` | `null` | no |
| [cloudwatch\_log\_group\_kms\_key\_id](#input\_cloudwatch\_log\_group\_kms\_key\_id) | If a KMS Key ARN is set, this key will be used to encrypt the corresponding log group. Please be sure that the KMS Key has an appropriate key policy (https://docs.aws.amazon.com/AmazonCloudWatch/latest/logs/encrypt-log-data-kms.html) | `string` | `null` | no |
| [cloudwatch\_log\_group\_name](#input\_cloudwatch\_log\_group\_name) | Custom name of CloudWatch Log Group for ECS cluster | `string` | `null` | no |
| [cloudwatch\_log\_group\_retention\_in\_days](#input\_cloudwatch\_log\_group\_retention\_in\_days) | Number of days to retain log events | `number` | `90` | no |
| [cloudwatch\_log\_group\_tags](#input\_cloudwatch\_log\_group\_tags) | A map of additional tags to add to the log group created | `map(string)` | `{}` | no |
+| [cluster\_capacity\_providers](#input\_cluster\_capacity\_providers) | List of capacity provider names to associate with the ECS cluster. Note: any capacity providers created by this module will be automatically added | `list(string)` | `[]` | no |
+| [cluster\_capacity\_providers\_wait\_duration](#input\_cluster\_capacity\_providers\_wait\_duration) | Duration to wait after the ECS cluster has become active before attaching the cluster capacity providers | `string` | `"20s"` | no |
| [configuration](#input\_configuration) | The execute command configuration for the cluster | object({
execute_command_configuration = optional(object({
kms_key_id = optional(string)
log_configuration = optional(object({
cloud_watch_encryption_enabled = optional(bool)
cloud_watch_log_group_name = optional(string)
s3_bucket_encryption_enabled = optional(bool)
s3_bucket_name = optional(string)
s3_kms_key_id = optional(string)
s3_key_prefix = optional(string)
}))
logging = optional(string, "OVERRIDE")
}))
managed_storage_configuration = optional(object({
fargate_ephemeral_storage_kms_key_id = optional(string)
kms_key_id = optional(string)
}))
}) | {
"execute_command_configuration": {
"log_configuration": {
"cloud_watch_log_group_name": "placeholder"
}
}
} | no |
| [create](#input\_create) | Determines whether resources will be created (affects all resources) | `bool` | `true` | no |
| [create\_cloudwatch\_log\_group](#input\_create\_cloudwatch\_log\_group) | Determines whether a log group is created by this module for the cluster logs. If not, AWS will automatically create one if logging is enabled | `bool` | `true` | no |
+| [create\_infrastructure\_iam\_role](#input\_create\_infrastructure\_iam\_role) | Determines whether the ECS infrastructure IAM role should be created | `bool` | `true` | no |
+| [create\_node\_iam\_instance\_profile](#input\_create\_node\_iam\_instance\_profile) | Determines whether an IAM instance profile is created or to use an existing IAM instance profile | `bool` | `true` | no |
+| [create\_security\_group](#input\_create\_security\_group) | Determines if a security group is created | `bool` | `true` | no |
| [create\_task\_exec\_iam\_role](#input\_create\_task\_exec\_iam\_role) | Determines whether the ECS task definition IAM role should be created | `bool` | `false` | no |
| [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)
})) | `{}` | no |
+| [disable\_v7\_default\_name\_description](#input\_disable\_v7\_default\_name\_description) | [DEPRECATED - will be removed in next breaking change] Determines whether to disable the default postfix added to resource names | `bool` | `false` | no |
+| [infrastructure\_iam\_role\_description](#input\_infrastructure\_iam\_role\_description) | Description of the role | `string` | `null` | no |
+| [infrastructure\_iam\_role\_name](#input\_infrastructure\_iam\_role\_name) | Name to use on IAM role created | `string` | `null` | no |
+| [infrastructure\_iam\_role\_override\_policy\_documents](#input\_infrastructure\_iam\_role\_override\_policy\_documents) | List of IAM policy documents that are merged together into the exported document. In merging, statements with non-blank `sid`s will override statements with the same `sid` | `list(string)` | `[]` | no |
+| [infrastructure\_iam\_role\_path](#input\_infrastructure\_iam\_role\_path) | IAM role path | `string` | `null` | no |
+| [infrastructure\_iam\_role\_permissions\_boundary](#input\_infrastructure\_iam\_role\_permissions\_boundary) | ARN of the policy that is used to set the permissions boundary for the IAM role | `string` | `null` | no |
+| [infrastructure\_iam\_role\_source\_policy\_documents](#input\_infrastructure\_iam\_role\_source\_policy\_documents) | List of IAM policy documents that are merged together into the exported document. Statements must have unique `sid`s | `list(string)` | `[]` | no |
+| [infrastructure\_iam\_role\_statements](#input\_infrastructure\_iam\_role\_statements) | A map of IAM policy [statements](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document#statement) for custom permission usage | map(object({
sid = optional(string)
actions = optional(list(string))
not_actions = optional(list(string))
effect = optional(string, "Allow")
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
variable = string
values = list(string)
})))
})) | `null` | no |
+| [infrastructure\_iam\_role\_tags](#input\_infrastructure\_iam\_role\_tags) | A map of additional tags to add to the IAM role created | `map(string)` | `{}` | no |
+| [infrastructure\_iam\_role\_use\_name\_prefix](#input\_infrastructure\_iam\_role\_use\_name\_prefix) | Determines whether the IAM role name (`iam_role_name`) is used as a prefix | `bool` | `true` | no |
| [name](#input\_name) | Name of the cluster (up to 255 letters, numbers, hyphens, and underscores) | `string` | `""` | no |
+| [node\_iam\_role\_additional\_policies](#input\_node\_iam\_role\_additional\_policies) | Additional policies to be added to the IAM role | `map(string)` | `{}` | no |
+| [node\_iam\_role\_description](#input\_node\_iam\_role\_description) | Description of the role | `string` | `"ECS Managed Instances node IAM role"` | no |
+| [node\_iam\_role\_name](#input\_node\_iam\_role\_name) | Name to use on IAM role/instance profile created | `string` | `null` | no |
+| [node\_iam\_role\_override\_policy\_documents](#input\_node\_iam\_role\_override\_policy\_documents) | List of IAM policy documents that are merged together into the exported document. In merging, statements with non-blank `sid`s will override statements with the same `sid` | `list(string)` | `[]` | no |
+| [node\_iam\_role\_path](#input\_node\_iam\_role\_path) | IAM role/instance profile path | `string` | `null` | no |
+| [node\_iam\_role\_permissions\_boundary](#input\_node\_iam\_role\_permissions\_boundary) | ARN of the policy that is used to set the permissions boundary for the IAM role | `string` | `null` | no |
+| [node\_iam\_role\_source\_policy\_documents](#input\_node\_iam\_role\_source\_policy\_documents) | List of IAM policy documents that are merged together into the exported document. Statements must have unique `sid`s | `list(string)` | `[]` | no |
+| [node\_iam\_role\_statements](#input\_node\_iam\_role\_statements) | A map of IAM policy [statements](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document#statement) for custom permission usage | map(object({
sid = optional(string)
actions = optional(list(string))
not_actions = optional(list(string))
effect = optional(string, "Allow")
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
variable = string
values = list(string)
})))
})) | `null` | no |
+| [node\_iam\_role\_tags](#input\_node\_iam\_role\_tags) | A map of additional tags to add to the IAM role/instance profile created | `map(string)` | `{}` | no |
+| [node\_iam\_role\_use\_name\_prefix](#input\_node\_iam\_role\_use\_name\_prefix) | Determines whether the IAM role/instance profile name (`node_iam_role_name`) is used as a prefix | `bool` | `true` | no |
| [region](#input\_region) | Region where the resource(s) will be managed. Defaults to the Region set in the provider configuration | `string` | `null` | no |
+| [security\_group\_description](#input\_security\_group\_description) | Description of the security group created | `string` | `null` | no |
+| [security\_group\_egress\_rules](#input\_security\_group\_egress\_rules) | Security group egress rules to add to the security group created | map(object({
name = optional(string)
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)
})) | {
"all_ipv4": {
"cidr_ipv4": "0.0.0.0/0",
"description": "Allow all IPv4 traffic",
"ip_protocol": "-1"
},
"all_ipv6": {
"cidr_ipv6": "::/0",
"description": "Allow all IPv6 traffic",
"ip_protocol": "-1"
}
} | no |
+| [security\_group\_ingress\_rules](#input\_security\_group\_ingress\_rules) | Security group ingress rules to add to the security group created | map(object({
name = optional(string)
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)
})) | `{}` | no |
+| [security\_group\_name](#input\_security\_group\_name) | Name to use on security group created | `string` | `null` | no |
+| [security\_group\_tags](#input\_security\_group\_tags) | A map of additional tags to add to the security group created | `map(string)` | `{}` | no |
+| [security\_group\_use\_name\_prefix](#input\_security\_group\_use\_name\_prefix) | Determines whether the security group name (`security_group_name`) is used as a prefix | `bool` | `true` | no |
| [service\_connect\_defaults](#input\_service\_connect\_defaults) | Configures a default Service Connect namespace | object({
namespace = string
}) | `null` | no |
| [setting](#input\_setting) | List of configuration block(s) with cluster settings. For example, this can be used to enable CloudWatch Container Insights for a cluster | list(object({
name = string
value = string
})) | [| no | | [tags](#input\_tags) | A map of tags to add to all resources | `map(string)` | `{}` | no | @@ -193,21 +324,31 @@ No modules. | [task\_exec\_iam\_statements](#input\_task\_exec\_iam\_statements) | A map of IAM policy [statements](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document#statement) for custom permission usage |
{
"name": "containerInsights",
"value": "enabled"
}
]
map(object({
sid = optional(string)
actions = optional(list(string))
not_actions = optional(list(string))
effect = optional(string, "Allow")
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
variable = string
values = list(string)
})))
})) | `null` | no |
| [task\_exec\_secret\_arns](#input\_task\_exec\_secret\_arns) | List of SecretsManager secret ARNs the task execution role will be permitted to get/read | `list(string)` | `[]` | no |
| [task\_exec\_ssm\_param\_arns](#input\_task\_exec\_ssm\_param\_arns) | List of SSM parameter ARNs the task execution role will be permitted to get/read | `list(string)` | `[]` | no |
+| [vpc\_id](#input\_vpc\_id) | The ID of the VPC where the security group will be created | `string` | `null` | no |
## Outputs
| Name | Description |
|------|-------------|
| [arn](#output\_arn) | ARN that identifies the cluster |
-| [autoscaling\_capacity\_providers](#output\_autoscaling\_capacity\_providers) | Map of autoscaling capacity providers created and their attributes |
+| [capacity\_providers](#output\_capacity\_providers) | Map of autoscaling capacity providers created and their attributes |
| [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 |
| [cluster\_capacity\_providers](#output\_cluster\_capacity\_providers) | Map of cluster capacity providers attributes |
| [id](#output\_id) | ID that identifies the cluster |
+| [infrastructure\_iam\_role\_arn](#output\_infrastructure\_iam\_role\_arn) | The Amazon Resource Name (ARN) specifying the IAM role |
+| [infrastructure\_iam\_role\_name](#output\_infrastructure\_iam\_role\_name) | IAM role name |
+| [infrastructure\_iam\_role\_unique\_id](#output\_infrastructure\_iam\_role\_unique\_id) | Stable and unique string identifying the IAM role |
| [name](#output\_name) | Name that identifies the cluster |
-| [task\_exec\_iam\_role\_arn](#output\_task\_exec\_iam\_role\_arn) | Task execution IAM role ARN |
-| [task\_exec\_iam\_role\_name](#output\_task\_exec\_iam\_role\_name) | Task execution IAM role name |
-| [task\_exec\_iam\_role\_unique\_id](#output\_task\_exec\_iam\_role\_unique\_id) | Stable and unique string identifying the task execution IAM role |
+| [node\_iam\_instance\_profile\_arn](#output\_node\_iam\_instance\_profile\_arn) | ARN assigned by AWS to the instance profile |
+| [node\_iam\_instance\_profile\_id](#output\_node\_iam\_instance\_profile\_id) | Instance profile's ID |
+| [node\_iam\_instance\_profile\_unique](#output\_node\_iam\_instance\_profile\_unique) | Stable and unique string identifying the IAM instance profile |
+| [node\_iam\_role\_arn](#output\_node\_iam\_role\_arn) | The Amazon Resource Name (ARN) specifying the IAM role |
+| [node\_iam\_role\_name](#output\_node\_iam\_role\_name) | IAM role name |
+| [node\_iam\_role\_unique\_id](#output\_node\_iam\_role\_unique\_id) | Stable and unique string identifying the IAM role |
+| [task\_exec\_iam\_role\_arn](#output\_task\_exec\_iam\_role\_arn) | The Amazon Resource Name (ARN) specifying the IAM role |
+| [task\_exec\_iam\_role\_name](#output\_task\_exec\_iam\_role\_name) | IAM role name |
+| [task\_exec\_iam\_role\_unique\_id](#output\_task\_exec\_iam\_role\_unique\_id) | Stable and unique string identifying the IAM role |
## License
diff --git a/modules/cluster/main.tf b/modules/cluster/main.tf
index 6eabcc3d..74c2d7df 100644
--- a/modules/cluster/main.tf
+++ b/modules/cluster/main.tf
@@ -1,3 +1,21 @@
+data "aws_region" "current" {
+ region = var.region
+
+ count = var.create ? 1 : 0
+}
+data "aws_partition" "current" {
+ count = var.create ? 1 : 0
+}
+data "aws_caller_identity" "current" {
+ count = var.create ? 1 : 0
+}
+
+locals {
+ account_id = try(data.aws_caller_identity.current[0].account_id, "")
+ partition = try(data.aws_partition.current[0].partition, "")
+ region = try(data.aws_region.current[0].region, "")
+}
+
################################################################################
# Cluster
################################################################################
@@ -95,16 +113,29 @@ resource "aws_cloudwatch_log_group" "this" {
# Cluster Capacity Providers
################################################################################
+# The managed instance capacity provider returns quickly in a `CREATING` state,
+# but we need to wait for it to be in the `ACTIVE` state before associating it with the cluster.
+resource "time_sleep" "this" {
+ count = var.create ? 1 : 0
+
+ create_duration = var.cluster_capacity_providers_wait_duration
+
+ triggers = {
+ # Triggers wants a string so we have to do some cheap serialization/deserialization to transport correctly
+ capacity_provider_names = var.capacity_providers != null ? join(",", [for k, v in var.capacity_providers : aws_ecs_capacity_provider.this[k].name]) : ""
+ # This is done so that the output of `capacity_providers` also waits for them to be `ACTIVE`
+ # for the scenarios where users define separate cluster and service modules (serivce needs the provider to be ACTIVE)
+ capacity_providers = var.capacity_providers != null ? jsonencode(aws_ecs_capacity_provider.this) : ""
+ }
+}
+
resource "aws_ecs_cluster_capacity_providers" "this" {
count = var.create ? 1 : 0
region = var.region
- cluster_name = aws_ecs_cluster.this[0].name
- capacity_providers = distinct(concat(
- [for k, v in var.default_capacity_provider_strategy : try(coalesce(v.name, k))],
- var.autoscaling_capacity_providers != null ? [for k, v in var.autoscaling_capacity_providers : try(coalesce(v.name, k))] : []
- ))
+ cluster_name = aws_ecs_cluster.this[0].name
+ capacity_providers = distinct(concat(var.cluster_capacity_providers, compact(split(",", time_sleep.this[0].triggers["capacity_provider_names"]))))
# https://docs.aws.amazon.com/AmazonECS/latest/developerguide/cluster-capacity-providers.html#capacity-providers-considerations
dynamic "default_capacity_provider_strategy" {
@@ -113,50 +144,208 @@ resource "aws_ecs_cluster_capacity_providers" "this" {
content {
base = default_capacity_provider_strategy.value.base
capacity_provider = try(coalesce(default_capacity_provider_strategy.value.name, default_capacity_provider_strategy.key))
- weight = default_capacity_provider_strategy.value.weight
+ weight = coalesce(default_capacity_provider_strategy.value.weight, 1)
}
}
-
- depends_on = [
- aws_ecs_capacity_provider.this
- ]
}
################################################################################
-# Capacity Provider - Autoscaling Group(s)
+# Capacity Provider
################################################################################
+locals {
+ managed_instances_enabled = var.capacity_providers != null ? anytrue([for k, v in var.capacity_providers : v.managed_instances_provider != null]) : false
+}
+
resource "aws_ecs_capacity_provider" "this" {
- for_each = var.create && var.autoscaling_capacity_providers != null ? var.autoscaling_capacity_providers : {}
+ for_each = var.create && var.capacity_providers != null ? var.capacity_providers : {}
region = var.region
- auto_scaling_group_provider {
- auto_scaling_group_arn = each.value.auto_scaling_group_arn
- managed_draining = each.value.managed_draining
+ dynamic "auto_scaling_group_provider" {
+ for_each = each.value.auto_scaling_group_provider != null ? [each.value.auto_scaling_group_provider] : []
- dynamic "managed_scaling" {
- for_each = each.value.managed_scaling != null ? [each.value.managed_scaling] : []
+ content {
+ auto_scaling_group_arn = auto_scaling_group_provider.value.auto_scaling_group_arn
+ managed_draining = auto_scaling_group_provider.value.managed_draining
+
+ dynamic "managed_scaling" {
+ for_each = auto_scaling_group_provider.value.managed_scaling != null ? [auto_scaling_group_provider.value.managed_scaling] : []
- content {
- instance_warmup_period = managed_scaling.value.instance_warmup_period
- maximum_scaling_step_size = managed_scaling.value.maximum_scaling_step_size
- minimum_scaling_step_size = managed_scaling.value.minimum_scaling_step_size
- status = managed_scaling.value.status
- target_capacity = managed_scaling.value.target_capacity
+ content {
+ instance_warmup_period = managed_scaling.value.instance_warmup_period
+ maximum_scaling_step_size = managed_scaling.value.maximum_scaling_step_size
+ minimum_scaling_step_size = managed_scaling.value.minimum_scaling_step_size
+ status = managed_scaling.value.status
+ target_capacity = managed_scaling.value.target_capacity
+ }
}
+
+ # When you use managed termination protection, you must also use managed scaling otherwise managed termination protection won't work
+ managed_termination_protection = auto_scaling_group_provider.value.managed_scaling != null ? auto_scaling_group_provider.value.managed_termination_protection : "DISABLED"
}
+ }
+
+ dynamic "managed_instances_provider" {
+ for_each = each.value.managed_instances_provider != null ? [each.value.managed_instances_provider] : []
+
+ content {
+ infrastructure_role_arn = local.create_infrastructure_iam_role ? aws_iam_role.infrastructure[0].arn : managed_instances_provider.value.infrastructure_role_arn
+
+ dynamic "instance_launch_template" {
+ for_each = managed_instances_provider.value.instance_launch_template != null ? [managed_instances_provider.value.instance_launch_template] : []
+
+ content {
+ ec2_instance_profile_arn = local.create_node_iam_instance_profile ? aws_iam_instance_profile.this[0].arn : instance_launch_template.value.ec2_instance_profile_arn
+
+ dynamic "instance_requirements" {
+ for_each = instance_launch_template.value.instance_requirements != null ? [instance_launch_template.value.instance_requirements] : []
+
+ content {
+ dynamic "accelerator_count" {
+ for_each = instance_requirements.value.accelerator_count != null ? [instance_requirements.value.accelerator_count] : []
+
+ content {
+ max = accelerator_count.value.max
+ min = accelerator_count.value.min
+ }
+ }
+
+ accelerator_manufacturers = instance_requirements.value.accelerator_manufacturers
+ accelerator_names = instance_requirements.value.accelerator_names
+
+ dynamic "accelerator_total_memory_mib" {
+ for_each = instance_requirements.value.accelerator_total_memory_mib != null ? [instance_requirements.value.accelerator_total_memory_mib] : []
+
+ content {
+ max = accelerator_total_memory_mib.value.max
+ min = accelerator_total_memory_mib.value.min
+ }
+ }
+
+ accelerator_types = instance_requirements.value.accelerator_types
+ allowed_instance_types = instance_requirements.value.allowed_instance_types
+ bare_metal = instance_requirements.value.bare_metal
+
+ dynamic "baseline_ebs_bandwidth_mbps" {
+ for_each = instance_requirements.value.baseline_ebs_bandwidth_mbps != null ? [instance_requirements.value.baseline_ebs_bandwidth_mbps] : []
+
+ content {
+ max = baseline_ebs_bandwidth_mbps.value.max
+ min = baseline_ebs_bandwidth_mbps.value.min
+ }
+ }
+
+ burstable_performance = instance_requirements.value.burstable_performance
+ cpu_manufacturers = instance_requirements.value.cpu_manufacturers
+ excluded_instance_types = instance_requirements.value.excluded_instance_types
+ instance_generations = instance_requirements.value.instance_generations
+ local_storage = instance_requirements.value.local_storage
+ local_storage_types = instance_requirements.value.local_storage_types
+ max_spot_price_as_percentage_of_optimal_on_demand_price = instance_requirements.value.max_spot_price_as_percentage_of_optimal_on_demand_price
+
+ dynamic "memory_gib_per_vcpu" {
+ for_each = instance_requirements.value.memory_gib_per_vcpu != null ? [instance_requirements.value.memory_gib_per_vcpu] : []
+
+ content {
+ max = memory_gib_per_vcpu.value.max
+ min = memory_gib_per_vcpu.value.min
+ }
+ }
+
+ dynamic "memory_mib" {
+ for_each = instance_requirements.value.memory_mib != null ? [instance_requirements.value.memory_mib] : []
+
+ content {
+ max = memory_mib.value.max
+ min = memory_mib.value.min
+ }
+ }
+
+ dynamic "network_bandwidth_gbps" {
+ for_each = instance_requirements.value.network_bandwidth_gbps != null ? [instance_requirements.value.network_bandwidth_gbps] : []
+
+ content {
+ max = network_bandwidth_gbps.value.max
+ min = network_bandwidth_gbps.value.min
+ }
+ }
+
+ dynamic "network_interface_count" {
+ for_each = instance_requirements.value.network_interface_count != null ? [instance_requirements.value.network_interface_count] : []
+
+ content {
+ max = network_interface_count.value.max
+ min = network_interface_count.value.min
+ }
+ }
+
+ on_demand_max_price_percentage_over_lowest_price = instance_requirements.value.on_demand_max_price_percentage_over_lowest_price
+ require_hibernate_support = instance_requirements.value.require_hibernate_support
+ spot_max_price_percentage_over_lowest_price = instance_requirements.value.spot_max_price_percentage_over_lowest_price
+
+ dynamic "total_local_storage_gb" {
+ for_each = instance_requirements.value.total_local_storage_gb != null ? [instance_requirements.value.total_local_storage_gb] : []
+
+ content {
+ max = total_local_storage_gb.value.max
+ min = total_local_storage_gb.value.min
+ }
+ }
+
+ dynamic "vcpu_count" {
+ for_each = instance_requirements.value.vcpu_count != null ? [instance_requirements.value.vcpu_count] : []
+
+ content {
+ max = vcpu_count.value.max
+ min = vcpu_count.value.min
+ }
+ }
+ }
+ }
+
+ monitoring = instance_launch_template.value.monitoring
- # When you use managed termination protection, you must also use managed scaling otherwise managed termination protection won't work
- managed_termination_protection = each.value.managed_scaling != null ? each.value.managed_termination_protection : "DISABLED"
+ dynamic "network_configuration" {
+ for_each = instance_launch_template.value.network_configuration != null ? [instance_launch_template.value.network_configuration] : []
+
+ content {
+ security_groups = local.create_security_group ? flatten(concat(aws_security_group.this[*].id, network_configuration.value.security_groups)) : network_configuration.value.security_groups
+ subnets = network_configuration.value.subnets
+ }
+ }
+
+ dynamic "storage_configuration" {
+ for_each = instance_launch_template.value.storage_configuration != null ? [instance_launch_template.value.storage_configuration] : []
+
+ content {
+ storage_size_gib = storage_configuration.value.storage_size_gib
+ }
+ }
+ }
+ }
+
+ propagate_tags = managed_instances_provider.value.propagate_tags
+ }
}
+ cluster = each.value.managed_instances_provider != null ? aws_ecs_cluster.this[0].name : null
+
name = try(coalesce(each.value.name, each.key), "")
tags = merge(
var.tags,
each.value.tags,
)
+
+ # What an awful friggin service API they created with managed instances
+ depends_on = [
+ aws_iam_role_policy_attachment.task_exec,
+ aws_iam_role_policy_attachment.infrastructure,
+ aws_iam_role_policy_attachment.node,
+ aws_vpc_security_group_ingress_rule.this,
+ aws_vpc_security_group_egress_rule.this,
+ ]
}
################################################################################
@@ -165,7 +354,7 @@ resource "aws_ecs_capacity_provider" "this" {
################################################################################
locals {
- task_exec_iam_role_name = try(coalesce(var.task_exec_iam_role_name, var.name), "")
+ task_exec_iam_role_name = try(coalesce(var.task_exec_iam_role_name, "${var.name}${var.disable_v7_default_name_description ? "" : "-task-exec"}"), "")
create_task_exec_iam_role = var.create && var.create_task_exec_iam_role
create_task_exec_policy = local.create_task_exec_iam_role && var.create_task_exec_policy
@@ -311,3 +500,523 @@ resource "aws_iam_role_policy_attachment" "task_exec" {
role = aws_iam_role.task_exec[0].name
policy_arn = aws_iam_policy.task_exec[0].arn
}
+
+############################################################################################
+# Infrastructure IAM role
+# https://docs.aws.amazon.com/AmazonECS/latest/developerguide/infrastructure_IAM_role.html
+############################################################################################
+
+locals {
+ create_infrastructure_iam_role = var.create && var.create_infrastructure_iam_role && local.managed_instances_enabled
+
+ infrastructure_iam_role_name = coalesce(var.infrastructure_iam_role_name, "${var.name}-infra")
+}
+
+data "aws_iam_policy_document" "infrastructure_assume" {
+ count = local.create_infrastructure_iam_role ? 1 : 0
+
+ statement {
+ sid = "ECSServiceAssumeRole"
+ actions = [
+ "sts:AssumeRole",
+ "sts:TagSession",
+ ]
+
+ principals {
+ type = "Service"
+ identifiers = ["ecs.amazonaws.com"]
+ }
+ }
+}
+
+resource "aws_iam_role" "infrastructure" {
+ count = local.create_infrastructure_iam_role ? 1 : 0
+
+ name = var.infrastructure_iam_role_use_name_prefix ? null : local.infrastructure_iam_role_name
+ name_prefix = var.infrastructure_iam_role_use_name_prefix ? "${local.infrastructure_iam_role_name}-" : null
+ path = var.infrastructure_iam_role_path
+ description = coalesce(var.infrastructure_iam_role_description, "Amazon ECS infrastructure IAM role that is used to manage your infrastructure (managed instances)")
+
+ assume_role_policy = data.aws_iam_policy_document.infrastructure_assume[0].json
+ permissions_boundary = var.infrastructure_iam_role_permissions_boundary
+ force_detach_policies = true
+
+ tags = merge(var.tags, var.infrastructure_iam_role_tags)
+}
+
+################################################################################
+# Infrastructure IAM role policy
+#
+# The managed policy requires role names to start with `ecsInstanceRole`
+# So we are duplicating the policy here to avoid that unfortunate and surprising requirement
+#
+# Ref: https://docs.aws.amazon.com/aws-managed-policy/latest/reference/AmazonECSInfrastructureRolePolicyForManagedInstances.html
+################################################################################
+
+data "aws_iam_policy_document" "infrastructure" {
+ count = local.create_infrastructure_iam_role ? 1 : 0
+
+ source_policy_documents = var.infrastructure_iam_role_source_policy_documents
+ override_policy_documents = var.infrastructure_iam_role_override_policy_documents
+
+ statement {
+ sid = "CreateLaunchTemplateForManagedInstances"
+ actions = ["ec2:CreateLaunchTemplate"]
+ resources = ["arn:${local.partition}:ec2:${local.region}:${local.account_id}:launch-template/*"]
+
+ condition {
+ test = "StringEquals"
+ variable = "aws:RequestTag/AmazonECSManaged"
+ values = [true]
+ }
+ }
+
+ statement {
+ sid = "CreateLaunchTemplateVersionsForManagedInstances"
+ actions = [
+ "ec2:CreateLaunchTemplateVersion",
+ "ec2:ModifyLaunchTemplate",
+ ]
+ resources = ["arn:${local.partition}:ec2:${local.region}:${local.account_id}:launch-template/*"]
+
+ condition {
+ test = "StringEquals"
+ variable = "ec2:ManagedResourceOperator"
+ values = ["ecs.amazonaws.com"]
+ }
+ }
+
+ statement {
+ sid = "ProvisionEC2InstancesForManagedInstances"
+ actions = ["ec2:CreateFleet"]
+ resources = [
+ "arn:${local.partition}:ec2:${local.region}:*:fleet/*",
+ "arn:${local.partition}:ec2:${local.region}:*:instance/*",
+ "arn:${local.partition}:ec2:${local.region}:*:network-interface/*",
+ "arn:${local.partition}:ec2:${local.region}:*:launch-template/*",
+ "arn:${local.partition}:ec2:${local.region}:*:security-group/*",
+ "arn:${local.partition}:ec2:${local.region}:*:subnet/*",
+ "arn:${local.partition}:ec2:${local.region}:*:volume/*",
+ "arn:${local.partition}:ec2:${local.region}:*:image/*",
+ ]
+
+ condition {
+ test = "StringEquals"
+ variable = "aws:RequestTag/AmazonECSManaged"
+ values = [true]
+ }
+ }
+
+ statement {
+ sid = "RunInstancesForManagedInstances"
+ actions = ["ec2:RunInstances"]
+ resources = [
+ "arn:${local.partition}:ec2:${local.region}:*:instance/*",
+ "arn:${local.partition}:ec2:${local.region}:*:volume/*",
+ "arn:${local.partition}:ec2:${local.region}:*:network-interface/*",
+ ]
+
+ condition {
+ test = "StringEquals"
+ variable = "aws:RequestTag/AmazonECSManaged"
+ values = [true]
+ }
+ }
+
+ statement {
+ sid = "RunInstancesForECSManagedLaunchTemplates"
+ actions = ["ec2:RunInstances"]
+ resources = ["arn:${local.partition}:ec2:${local.region}:*:launch-template/*"]
+
+ condition {
+ test = "StringEquals"
+ variable = "ec2:ResourceTag/AmazonECSManaged"
+ values = [true]
+ }
+ }
+
+ statement {
+ sid = "RunInstancesForSupportingResources"
+ actions = ["ec2:RunInstances"]
+ resources = [
+ "arn:${local.partition}:ec2:${local.region}:*:subnet/*",
+ "arn:${local.partition}:ec2:${local.region}:*:security-group/*",
+ "arn:${local.partition}:ec2:${local.region}:*:image/*",
+ ]
+ }
+
+ statement {
+ sid = "TagOnCreateEC2ResourcesForManagedInstances"
+ actions = ["ec2:CreateTags"]
+ resources = [
+ "arn:${local.partition}:ec2:${local.region}:*:fleet/*",
+ "arn:${local.partition}:ec2:${local.region}:*:launch-template/*",
+ "arn:${local.partition}:ec2:${local.region}:*:network-interface/*",
+ "arn:${local.partition}:ec2:${local.region}:*:instance/*",
+ "arn:${local.partition}:ec2:${local.region}:*:volume/*",
+ ]
+
+ condition {
+ test = "StringEquals"
+ variable = "ec2:CreateAction"
+ values = [
+ "CreateFleet",
+ "CreateLaunchTemplate",
+ "RunInstances",
+ ]
+ }
+ }
+
+ statement {
+ sid = "PassInstanceRoleForManagedInstances"
+ actions = ["iam:PassRole"]
+ resources = [aws_iam_role.node[0].arn]
+
+ condition {
+ test = "StringEquals"
+ variable = "iam:PassedToService"
+ values = ["ec2.amazonaws.com"]
+ }
+ }
+
+ statement {
+ sid = "CreateServiceLinkedRoleForEC2Spot"
+ actions = ["iam:CreateServiceLinkedRole"]
+ resources = ["arn:${local.partition}:iam::${local.account_id}:role/aws-service-role/spot.amazonaws.com/AWSServiceRoleForEC2Spot"]
+ }
+
+ statement {
+ sid = "DescribeEC2ResourcesManagedByECS"
+ actions = [
+ "ec2:DescribeInstances",
+ "ec2:DescribeInstanceTypes",
+ "ec2:DescribeLaunchTemplates",
+ "ec2:DescribeNetworkInterfaces",
+ "ec2:DescribeInstanceTypeOfferings",
+ "ec2:DescribeAvailabilityZones",
+ "ec2:DescribeSecurityGroups",
+ "ec2:DescribeSubnets",
+ "ec2:DescribeVpcs",
+ ]
+ resources = ["*"]
+ }
+
+ dynamic "statement" {
+ for_each = var.infrastructure_iam_role_statements != null ? var.infrastructure_iam_role_statements : {}
+
+ content {
+ sid = try(coalesce(statement.value.sid, statement.key))
+ actions = statement.value.actions
+ not_actions = statement.value.not_actions
+ effect = statement.value.effect
+ resources = statement.value.resources
+ not_resources = statement.value.not_resources
+
+ dynamic "principals" {
+ for_each = statement.value.principals != null ? statement.value.principals : []
+
+ content {
+ type = principals.value.type
+ identifiers = principals.value.identifiers
+ }
+ }
+
+ dynamic "not_principals" {
+ for_each = statement.value.not_principals != null ? statement.value.not_principals : []
+
+ content {
+ type = not_principals.value.type
+ identifiers = not_principals.value.identifiers
+ }
+ }
+
+ dynamic "condition" {
+ for_each = statement.value.condition != null ? statement.value.condition : []
+
+ content {
+ test = condition.value.test
+ values = condition.value.values
+ variable = condition.value.variable
+ }
+ }
+ }
+ }
+}
+
+resource "aws_iam_policy" "infrastructure" {
+ count = local.create_infrastructure_iam_role ? 1 : 0
+
+ name = var.infrastructure_iam_role_use_name_prefix ? null : local.infrastructure_iam_role_name
+ name_prefix = var.infrastructure_iam_role_use_name_prefix ? "${local.infrastructure_iam_role_name}-" : null
+ description = coalesce(var.infrastructure_iam_role_description, "ECS Managed Instances infrastructure role permissions")
+ policy = data.aws_iam_policy_document.infrastructure[0].json
+
+ tags = merge(var.tags, var.infrastructure_iam_role_tags)
+}
+
+resource "aws_iam_role_policy_attachment" "infrastructure" {
+ count = local.create_infrastructure_iam_role ? 1 : 0
+
+ policy_arn = aws_iam_policy.infrastructure[0].arn
+ role = aws_iam_role.infrastructure[0].name
+}
+
+################################################################################
+# Node IAM role
+################################################################################
+
+locals {
+ create_node_iam_instance_profile = var.create && var.create_node_iam_instance_profile && local.managed_instances_enabled
+
+ node_iam_role_name = coalesce(var.node_iam_role_name, "${var.name}-node")
+}
+
+data "aws_iam_policy_document" "node_assume_role_policy" {
+ count = local.create_node_iam_instance_profile ? 1 : 0
+
+ statement {
+ sid = "ECSNodeAssumeRole"
+ actions = [
+ "sts:AssumeRole",
+ "sts:TagSession",
+ ]
+
+ principals {
+ type = "Service"
+ identifiers = ["ec2.amazonaws.com"]
+ }
+ }
+}
+
+resource "aws_iam_role" "node" {
+ count = local.create_node_iam_instance_profile ? 1 : 0
+
+ name = var.node_iam_role_use_name_prefix ? null : local.node_iam_role_name
+ name_prefix = var.node_iam_role_use_name_prefix ? "${local.node_iam_role_name}-" : null
+ path = var.node_iam_role_path
+ description = coalesce(var.node_iam_role_description, "Amazon ECS managed instance node role for ECS cluster ${var.name}")
+
+ assume_role_policy = data.aws_iam_policy_document.node_assume_role_policy[0].json
+ permissions_boundary = var.node_iam_role_permissions_boundary
+ force_detach_policies = true
+
+ tags = merge(var.tags, var.node_iam_role_tags)
+}
+
+resource "aws_iam_role_policy_attachment" "node_additional" {
+ for_each = { for k, v in var.node_iam_role_additional_policies : k => v if local.create_node_iam_instance_profile }
+
+ policy_arn = each.value
+ role = aws_iam_role.node[0].name
+}
+
+################################################################################
+# Node IAM role policy
+#
+# Due to this warning from ECS documentation
+# https://docs.aws.amazon.com/AmazonECS/latest/developerguide/managed-instances-instance-profile.html
+#
+# > If you are using Amazon ECS Managed Instances with the AWS-managed Infrastructure policy,
+# > the instance profile must be named ecsInstanceRole. If you are using a custom policy for
+# > the Infrastructure role, the instance profile can have an alternative name.
+#
+# We default to creating the policy in order to remove this "surprising" requirement
+# Ref: docs.aws.amazon.com/aws-managed-policy/latest/reference/AmazonECSInstanceRolePolicyForManagedInstances.html
+################################################################################
+
+data "aws_iam_policy_document" "node" {
+ count = local.create_node_iam_instance_profile ? 1 : 0
+
+ source_policy_documents = var.node_iam_role_source_policy_documents
+ override_policy_documents = var.node_iam_role_override_policy_documents
+
+ statement {
+ sid = "ECSAgentDiscoverPollEndpointPermissions"
+ actions = ["ecs:DiscoverPollEndpoint"]
+ resources = ["*"]
+ }
+
+ statement {
+ sid = "ECSAgentRegisterPermissions"
+ actions = ["ecs:RegisterContainerInstance"]
+ resources = [aws_ecs_cluster.this[0].arn]
+ }
+
+ statement {
+ sid = "ECSAgentPollPermissions"
+ actions = ["ecs:Poll"]
+ resources = ["arn:${local.partition}:ecs:${local.region}:${local.account_id}:container-instance/*"]
+ }
+
+ statement {
+ sid = "ECSAgentTelemetryPermissions"
+ actions = [
+ "ecs:StartTelemetrySession",
+ "ecs:PutSystemLogEvents",
+ ]
+ resources = ["arn:${local.partition}:ecs:${local.region}:${local.account_id}:container-instance/*"]
+ }
+
+ statement {
+ sid = "ECSAgentStateChangePermissions"
+ actions = [
+ "ecs:SubmitAttachmentStateChanges",
+ "ecs:SubmitTaskStateChange",
+ ]
+ resources = [aws_ecs_cluster.this[0].arn]
+ }
+
+ dynamic "statement" {
+ for_each = var.node_iam_role_statements != null ? var.node_iam_role_statements : {}
+
+ content {
+ sid = try(coalesce(statement.value.sid, statement.key))
+ actions = statement.value.actions
+ not_actions = statement.value.not_actions
+ effect = statement.value.effect
+ resources = statement.value.resources
+ not_resources = statement.value.not_resources
+
+ dynamic "principals" {
+ for_each = statement.value.principals != null ? statement.value.principals : []
+
+ content {
+ type = principals.value.type
+ identifiers = principals.value.identifiers
+ }
+ }
+
+ dynamic "not_principals" {
+ for_each = statement.value.not_principals != null ? statement.value.not_principals : []
+
+ content {
+ type = not_principals.value.type
+ identifiers = not_principals.value.identifiers
+ }
+ }
+
+ dynamic "condition" {
+ for_each = statement.value.condition != null ? statement.value.condition : []
+
+ content {
+ test = condition.value.test
+ values = condition.value.values
+ variable = condition.value.variable
+ }
+ }
+ }
+ }
+}
+
+resource "aws_iam_policy" "node" {
+ count = local.create_node_iam_instance_profile ? 1 : 0
+
+ name = var.node_iam_role_use_name_prefix ? null : local.node_iam_role_name
+ name_prefix = var.node_iam_role_use_name_prefix ? "${local.node_iam_role_name}-" : null
+ description = coalesce(var.node_iam_role_description, "ECS Managed Instances permissions")
+ policy = data.aws_iam_policy_document.node[0].json
+
+ tags = merge(var.tags, var.node_iam_role_tags)
+}
+
+resource "aws_iam_role_policy_attachment" "node" {
+ count = local.create_node_iam_instance_profile ? 1 : 0
+
+ policy_arn = aws_iam_policy.node[0].arn
+ role = aws_iam_role.node[0].name
+}
+
+################################################################################
+# Node Instance Profile
+################################################################################
+
+resource "aws_iam_instance_profile" "this" {
+ count = local.create_node_iam_instance_profile ? 1 : 0
+
+ role = aws_iam_role.node[0].name
+
+ name = var.node_iam_role_use_name_prefix ? null : local.node_iam_role_name
+ name_prefix = var.node_iam_role_use_name_prefix ? "${local.node_iam_role_name}-" : null
+ path = var.node_iam_role_path
+
+ tags = merge(var.tags, var.node_iam_role_tags)
+
+ lifecycle {
+ create_before_destroy = true
+ }
+}
+
+################################################################################
+# Security Group
+################################################################################
+
+locals {
+ create_security_group = var.create && var.create_security_group && local.managed_instances_enabled
+
+ security_group_name = coalesce(var.security_group_name, "${var.name}${var.disable_v7_default_name_description ? "" : "-cluster"}")
+}
+
+resource "aws_security_group" "this" {
+ count = local.create_security_group ? 1 : 0
+
+ region = var.region
+
+ name = var.security_group_use_name_prefix ? null : local.security_group_name
+ name_prefix = var.security_group_use_name_prefix ? "${local.security_group_name}-" : null
+ description = coalesce(var.security_group_description, "Security group for ECS managed instances in cluster ${aws_ecs_cluster.this[0].name}")
+ vpc_id = var.vpc_id
+
+ tags = merge(
+ var.tags,
+ { Name = local.security_group_name },
+ var.security_group_tags
+ )
+
+ lifecycle {
+ create_before_destroy = true
+ }
+}
+
+resource "aws_vpc_security_group_ingress_rule" "this" {
+ for_each = { for k, v in var.security_group_ingress_rules : k => v if var.security_group_ingress_rules != null && local.create_security_group }
+
+ region = var.region
+
+ cidr_ipv4 = each.value.cidr_ipv4
+ cidr_ipv6 = each.value.cidr_ipv6
+ description = each.value.description
+ from_port = each.value.from_port
+ ip_protocol = each.value.ip_protocol
+ prefix_list_id = each.value.prefix_list_id
+ referenced_security_group_id = each.value.referenced_security_group_id == "self" ? aws_security_group.this[0].id : each.value.referenced_security_group_id
+ security_group_id = aws_security_group.this[0].id
+ tags = merge(
+ var.tags,
+ var.security_group_tags,
+ { "Name" = coalesce(each.value.name, "${local.security_group_name}-${each.key}") },
+ each.value.tags
+ )
+ to_port = try(coalesce(each.value.to_port, each.value.from_port), null)
+}
+
+resource "aws_vpc_security_group_egress_rule" "this" {
+ for_each = { for k, v in var.security_group_egress_rules : k => v if var.security_group_egress_rules != null && local.create_security_group }
+
+ region = var.region
+
+ cidr_ipv4 = each.value.cidr_ipv4
+ cidr_ipv6 = each.value.cidr_ipv6
+ description = each.value.description
+ from_port = try(coalesce(each.value.from_port, each.value.to_port), null)
+ ip_protocol = each.value.ip_protocol
+ prefix_list_id = each.value.prefix_list_id
+ referenced_security_group_id = each.value.referenced_security_group_id == "self" ? aws_security_group.this[0].id : each.value.referenced_security_group_id
+ security_group_id = aws_security_group.this[0].id
+ tags = merge(
+ var.tags,
+ var.security_group_tags,
+ { "Name" = coalesce(each.value.name, "${local.security_group_name}-${each.key}") },
+ each.value.tags
+ )
+ to_port = each.value.to_port
+}
diff --git a/modules/cluster/outputs.tf b/modules/cluster/outputs.tf
index 4e05381f..5a9e505a 100644
--- a/modules/cluster/outputs.tf
+++ b/modules/cluster/outputs.tf
@@ -44,9 +44,9 @@ output "cluster_capacity_providers" {
# Capacity Provider - Autoscaling Group(s)
################################################################################
-output "autoscaling_capacity_providers" {
+output "capacity_providers" {
description = "Map of autoscaling capacity providers created and their attributes"
- value = aws_ecs_capacity_provider.this
+ value = var.capacity_providers != null ? jsondecode(time_sleep.this[0].triggers["capacity_providers"]) : {}
}
################################################################################
@@ -54,17 +54,74 @@ output "autoscaling_capacity_providers" {
# https://docs.aws.amazon.com/AmazonECS/latest/developerguide/task_execution_IAM_role.html
################################################################################
-output "task_exec_iam_role_name" {
- description = "Task execution IAM role name"
- value = try(aws_iam_role.task_exec[0].name, null)
-}
-
output "task_exec_iam_role_arn" {
- description = "Task execution IAM role ARN"
+ description = "The Amazon Resource Name (ARN) specifying the IAM role"
value = try(aws_iam_role.task_exec[0].arn, null)
}
+output "task_exec_iam_role_name" {
+ description = "IAM role name"
+ value = try(aws_iam_role.task_exec[0].name, null)
+}
+
output "task_exec_iam_role_unique_id" {
- description = "Stable and unique string identifying the task execution IAM role"
+ description = "Stable and unique string identifying the IAM role"
value = try(aws_iam_role.task_exec[0].unique_id, null)
}
+
+############################################################################################
+# Infrastructure IAM role
+############################################################################################
+
+output "infrastructure_iam_role_arn" {
+ description = "The Amazon Resource Name (ARN) specifying the IAM role"
+ value = try(aws_iam_role.infrastructure[0].arn, null)
+}
+
+output "infrastructure_iam_role_name" {
+ description = "IAM role name"
+ value = try(aws_iam_role.infrastructure[0].name, null)
+}
+
+output "infrastructure_iam_role_unique_id" {
+ description = "Stable and unique string identifying the IAM role"
+ value = try(aws_iam_role.infrastructure[0].unique_id, null)
+}
+
+################################################################################
+# Node IAM role
+################################################################################
+
+output "node_iam_role_arn" {
+ description = "The Amazon Resource Name (ARN) specifying the IAM role"
+ value = try(aws_iam_role.node[0].arn, null)
+}
+
+output "node_iam_role_name" {
+ description = "IAM role name"
+ value = try(aws_iam_role.node[0].name, null)
+}
+
+output "node_iam_role_unique_id" {
+ description = "Stable and unique string identifying the IAM role"
+ value = try(aws_iam_role.node[0].unique_id, null)
+}
+
+################################################################################
+# IAM Instance Profile
+################################################################################
+
+output "node_iam_instance_profile_arn" {
+ description = "ARN assigned by AWS to the instance profile"
+ value = try(aws_iam_instance_profile.this[0].arn, null)
+}
+
+output "node_iam_instance_profile_id" {
+ description = "Instance profile's ID"
+ value = try(aws_iam_instance_profile.this[0].id, null)
+}
+
+output "node_iam_instance_profile_unique" {
+ description = "Stable and unique string identifying the IAM instance profile"
+ value = try(aws_iam_instance_profile.this[0].unique_id, null)
+}
diff --git a/modules/cluster/variables.tf b/modules/cluster/variables.tf
index 495e5dda..a7e49761 100644
--- a/modules/cluster/variables.tf
+++ b/modules/cluster/variables.tf
@@ -16,6 +16,12 @@ variable "tags" {
default = {}
}
+variable "disable_v7_default_name_description" {
+ description = "[DEPRECATED - will be removed in next breaking change] Determines whether to disable the default postfix added to resource names"
+ type = bool
+ default = false
+}
+
################################################################################
# Cluster
################################################################################
@@ -121,21 +127,103 @@ variable "cloudwatch_log_group_tags" {
# Capacity Providers
################################################################################
-variable "autoscaling_capacity_providers" {
- description = "Map of autoscaling capacity provider definitions to create for the cluster"
+variable "cluster_capacity_providers_wait_duration" {
+ description = "Duration to wait after the ECS cluster has become active before attaching the cluster capacity providers"
+ type = string
+ default = "20s"
+}
+
+variable "cluster_capacity_providers" {
+ description = "List of capacity provider names to associate with the ECS cluster. Note: any capacity providers created by this module will be automatically added"
+ type = list(string)
+ default = []
+}
+
+variable "capacity_providers" {
+ description = "Map of capacity provider definitions to create"
type = map(object({
- auto_scaling_group_arn = string
- managed_draining = optional(string, "ENABLED")
- managed_scaling = optional(object({
- instance_warmup_period = optional(number)
- maximum_scaling_step_size = optional(number)
- minimum_scaling_step_size = optional(number)
- status = optional(string)
- target_capacity = optional(number)
+ auto_scaling_group_provider = optional(object({
+ auto_scaling_group_arn = string
+ managed_draining = optional(string, "ENABLED")
+ managed_scaling = optional(object({
+ instance_warmup_period = optional(number)
+ maximum_scaling_step_size = optional(number)
+ minimum_scaling_step_size = optional(number)
+ status = optional(string)
+ target_capacity = optional(number)
+ }))
+ managed_termination_protection = optional(string)
+ }))
+ managed_instances_provider = optional(object({
+ infrastructure_role_arn = optional(string)
+ instance_launch_template = object({
+ ec2_instance_profile_arn = optional(string)
+ instance_requirements = optional(object({
+ accelerator_count = optional(object({
+ max = optional(number)
+ min = optional(number)
+ }))
+ accelerator_manufacturers = optional(list(string))
+ accelerator_names = optional(list(string))
+ accelerator_total_memory_mib = optional(object({
+ max = optional(number)
+ min = optional(number)
+ }))
+ accelerator_types = optional(list(string))
+ allowed_instance_types = optional(list(string))
+ bare_metal = optional(string)
+ baseline_ebs_bandwidth_mbps = optional(object({
+ max = optional(number)
+ min = optional(number)
+ }))
+ burstable_performance = optional(string)
+ cpu_manufacturers = optional(list(string))
+ excluded_instance_types = optional(list(string))
+ instance_generations = optional(list(string))
+ local_storage = optional(string)
+ local_storage_types = optional(list(string))
+ max_spot_price_as_percentage_of_optimal_on_demand_price = optional(number)
+ memory_gib_per_vcpu = optional(object({
+ max = optional(number)
+ min = optional(number)
+ }))
+ memory_mib = optional(object({
+ max = optional(number)
+ min = optional(number)
+ }))
+ network_bandwidth_gbps = optional(object({
+ max = optional(number)
+ min = optional(number)
+ }))
+ network_interface_count = optional(object({
+ max = optional(number)
+ min = optional(number)
+ }))
+ on_demand_max_price_percentage_over_lowest_price = optional(number)
+ require_hibernate_support = optional(bool)
+ spot_max_price_percentage_over_lowest_price = optional(number)
+ total_local_storage_gb = optional(object({
+ max = optional(number)
+ min = optional(number)
+ }))
+ vcpu_count = optional(object({
+ max = optional(number)
+ min = optional(number)
+ }))
+ }))
+ monitoring = optional(string)
+ network_configuration = optional(object({
+ security_groups = optional(list(string), [])
+ subnets = list(string)
+ }))
+ storage_configuration = optional(object({
+ storage_size_gib = number
+ }))
+ })
+ propagate_tags = optional(string, "CAPACITY_PROVIDER")
}))
- managed_termination_protection = optional(string)
- name = optional(string) # Will fall back to use map key if not set
- tags = optional(map(string), {})
+ name = optional(string) # Will fall back to use map key if not set
+ tags = optional(map(string), {})
}))
default = null
}
@@ -247,3 +335,285 @@ variable "task_exec_iam_statements" {
}))
default = null
}
+
+############################################################################################
+# Infrastructure IAM role
+############################################################################################
+
+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_name" {
+ description = "Name to use on IAM role created"
+ type = string
+ default = null
+}
+
+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" {
+ description = "IAM role path"
+ type = string
+ default = null
+}
+
+variable "infrastructure_iam_role_description" {
+ description = "Description of the role"
+ type = string
+ default = null
+}
+
+variable "infrastructure_iam_role_permissions_boundary" {
+ description = "ARN of the policy that is used to set the permissions boundary for the IAM role"
+ type = string
+ default = null
+}
+
+variable "infrastructure_iam_role_tags" {
+ description = "A map of additional tags to add to the IAM role created"
+ type = map(string)
+ default = {}
+ nullable = false
+}
+
+################################################################################
+# Infrastructure IAM role policy
+################################################################################
+
+variable "infrastructure_iam_role_source_policy_documents" {
+ description = "List of IAM policy documents that are merged together into the exported document. Statements must have unique `sid`s"
+ type = list(string)
+ default = []
+}
+
+variable "infrastructure_iam_role_override_policy_documents" {
+ description = "List of IAM policy documents that are merged together into the exported document. In merging, statements with non-blank `sid`s will override statements with the same `sid`"
+ type = list(string)
+ default = []
+}
+
+variable "infrastructure_iam_role_statements" {
+ description = "A map of IAM policy [statements](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document#statement) for custom permission usage"
+ type = map(object({
+ sid = optional(string)
+ actions = optional(list(string))
+ not_actions = optional(list(string))
+ effect = optional(string, "Allow")
+ 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
+ variable = string
+ values = list(string)
+ })))
+ }))
+ default = null
+}
+
+################################################################################
+# Node IAM role & instance profile
+################################################################################
+
+variable "create_node_iam_instance_profile" {
+ description = "Determines whether an IAM instance profile is created or to use an existing IAM instance profile"
+ type = bool
+ default = true
+ nullable = false
+}
+
+variable "node_iam_role_name" {
+ description = "Name to use on IAM role/instance profile created"
+ type = string
+ default = null
+}
+
+variable "node_iam_role_use_name_prefix" {
+ description = "Determines whether the IAM role/instance profile name (`node_iam_role_name`) is used as a prefix"
+ type = bool
+ default = true
+ nullable = false
+}
+
+variable "node_iam_role_path" {
+ description = "IAM role/instance profile path"
+ type = string
+ default = null
+}
+
+variable "node_iam_role_description" {
+ description = "Description of the role"
+ type = string
+ default = "ECS Managed Instances node IAM role"
+ nullable = false
+}
+
+variable "node_iam_role_permissions_boundary" {
+ description = "ARN of the policy that is used to set the permissions boundary for the IAM role"
+ type = string
+ default = null
+}
+
+variable "node_iam_role_additional_policies" {
+ description = "Additional policies to be added to the IAM role"
+ type = map(string)
+ default = {}
+ nullable = false
+}
+
+variable "node_iam_role_tags" {
+ description = "A map of additional tags to add to the IAM role/instance profile created"
+ type = map(string)
+ default = {}
+ nullable = false
+}
+
+################################################################################
+# Node IAM role policy
+################################################################################
+
+variable "node_iam_role_source_policy_documents" {
+ description = "List of IAM policy documents that are merged together into the exported document. Statements must have unique `sid`s"
+ type = list(string)
+ default = []
+}
+
+variable "node_iam_role_override_policy_documents" {
+ description = "List of IAM policy documents that are merged together into the exported document. In merging, statements with non-blank `sid`s will override statements with the same `sid`"
+ type = list(string)
+ default = []
+}
+
+variable "node_iam_role_statements" {
+ description = "A map of IAM policy [statements](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document#statement) for custom permission usage"
+ type = map(object({
+ sid = optional(string)
+ actions = optional(list(string))
+ not_actions = optional(list(string))
+ effect = optional(string, "Allow")
+ 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
+ variable = string
+ values = list(string)
+ })))
+ }))
+ default = null
+}
+
+################################################################################
+# Security Group
+################################################################################
+
+variable "create_security_group" {
+ description = "Determines if a security group is created"
+ type = bool
+ default = true
+ nullable = false
+}
+
+variable "vpc_id" {
+ description = "The ID of the VPC where the security group will be created"
+ type = string
+ default = null
+}
+
+variable "security_group_name" {
+ description = "Name to use on security group created"
+ type = string
+ default = null
+}
+
+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" {
+ description = "Description of the security group created"
+ type = string
+ default = null
+}
+
+variable "security_group_ingress_rules" {
+ description = "Security group ingress rules to add to the security group created"
+ type = map(object({
+ name = optional(string)
+
+ 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 = {}
+ nullable = false
+}
+
+variable "security_group_egress_rules" {
+ description = "Security group egress rules to add to the security group created"
+ type = map(object({
+ name = optional(string)
+
+ 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 = {
+ all_ipv4 = {
+ cidr_ipv4 = "0.0.0.0/0"
+ description = "Allow all IPv4 traffic"
+ ip_protocol = "-1"
+ }
+ all_ipv6 = {
+ cidr_ipv6 = "::/0"
+ description = "Allow all IPv6 traffic"
+ ip_protocol = "-1"
+ }
+ }
+ 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
+}
diff --git a/modules/cluster/versions.tf b/modules/cluster/versions.tf
index 70f23a44..bceb79a7 100644
--- a/modules/cluster/versions.tf
+++ b/modules/cluster/versions.tf
@@ -4,7 +4,11 @@ terraform {
required_providers {
aws = {
source = "hashicorp/aws"
- version = ">= 6.21"
+ version = ">= 6.23"
+ }
+ time = {
+ source = "hashicorp/time"
+ version = ">= 0.13"
}
}
}
diff --git a/modules/container-definition/README.md b/modules/container-definition/README.md
index 2eab9fec..acbbc571 100644
--- a/modules/container-definition/README.md
+++ b/modules/container-definition/README.md
@@ -106,9 +106,11 @@ module "example_ecs_container_definition" {
## Examples
-- [ECS Cluster Complete](https://github.com/terraform-aws-modules/terraform-aws-ecs/tree/master/examples/complete)
-- [ECS Cluster w/ EC2 Autoscaling Capacity Provider](https://github.com/terraform-aws-modules/terraform-aws-ecs/tree/master/examples/ec2-autoscaling)
-- [ECS Cluster w/ Fargate Capacity Provider](https://github.com/terraform-aws-modules/terraform-aws-ecs/tree/master/examples/fargate)
+- [ECS cluster w/ integrated service(s)](https://github.com/terraform-aws-modules/terraform-aws-ecs/tree/master/examples/complete)
+- [ECS container definition](https://github.com/terraform-aws-modules/terraform-aws-ecs/tree/master/examples/container-definition)
+- [ECS cluster w/ EC2 Autoscaling capacity provider](https://github.com/terraform-aws-modules/terraform-aws-ecs/tree/master/examples/ec2-autoscaling)
+- [ECS cluster w/ Fargate capacity provider](https://github.com/terraform-aws-modules/terraform-aws-ecs/tree/master/examples/fargate)
+- [ECS cluster w/ ECS managed instances capacity provider](https://github.com/terraform-aws-modules/terraform-aws-ecs/tree/master/examples/managed-instances)
## Requirements
@@ -116,13 +118,13 @@ module "example_ecs_container_definition" {
| Name | Version |
|------|---------|
| [terraform](#requirement\_terraform) | >= 1.5.7 |
-| [aws](#requirement\_aws) | >= 6.21 |
+| [aws](#requirement\_aws) | >= 6.23 |
## Providers
| Name | Version |
|------|---------|
-| [aws](#provider\_aws) | >= 6.21 |
+| [aws](#provider\_aws) | >= 6.23 |
## Modules
diff --git a/modules/container-definition/versions.tf b/modules/container-definition/versions.tf
index 70f23a44..7e5b918f 100644
--- a/modules/container-definition/versions.tf
+++ b/modules/container-definition/versions.tf
@@ -4,7 +4,7 @@ terraform {
required_providers {
aws = {
source = "hashicorp/aws"
- version = ">= 6.21"
+ version = ">= 6.23"
}
}
}
diff --git a/modules/service/README.md b/modules/service/README.md
index f0940eb1..c4ecd038 100644
--- a/modules/service/README.md
+++ b/modules/service/README.md
@@ -3,6 +3,7 @@
Configuration in this directory creates an Amazon ECS Service and associated resources.
Some notable configurations to be aware of when using this module:
+
1. `desired_count`/`scale` is always ignored; the module is designed to utilize autoscaling by default (though it can be disabled)
2. The default configuration is intended for `FARGATE` use
@@ -160,9 +161,11 @@ module "ecs_service" {
## Examples
-- [ECS Cluster Complete](https://github.com/terraform-aws-modules/terraform-aws-ecs/tree/master/examples/complete)
-- [ECS Cluster w/ EC2 Autoscaling Capacity Provider](https://github.com/terraform-aws-modules/terraform-aws-ecs/tree/master/examples/ec2-autoscaling)
-- [ECS Cluster w/ Fargate Capacity Provider](https://github.com/terraform-aws-modules/terraform-aws-ecs/tree/master/examples/fargate)
+- [ECS cluster w/ integrated service(s)](https://github.com/terraform-aws-modules/terraform-aws-ecs/tree/master/examples/complete)
+- [ECS container definition](https://github.com/terraform-aws-modules/terraform-aws-ecs/tree/master/examples/container-definition)
+- [ECS cluster w/ EC2 Autoscaling capacity provider](https://github.com/terraform-aws-modules/terraform-aws-ecs/tree/master/examples/ec2-autoscaling)
+- [ECS cluster w/ Fargate capacity provider](https://github.com/terraform-aws-modules/terraform-aws-ecs/tree/master/examples/fargate)
+- [ECS cluster w/ ECS managed instances capacity provider](https://github.com/terraform-aws-modules/terraform-aws-ecs/tree/master/examples/managed-instances)
## Requirements
@@ -170,13 +173,13 @@ module "ecs_service" {
| Name | Version |
|------|---------|
| [terraform](#requirement\_terraform) | >= 1.5.7 |
-| [aws](#requirement\_aws) | >= 6.21 |
+| [aws](#requirement\_aws) | >= 6.23 |
## Providers
| Name | Version |
|------|---------|
-| [aws](#provider\_aws) | >= 6.21 |
+| [aws](#provider\_aws) | >= 6.23 |
## Modules
@@ -204,6 +207,7 @@ module "ecs_service" {
| [aws_iam_role.task_exec](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role) | resource |
| [aws_iam_role.tasks](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role) | resource |
| [aws_iam_role_policy_attachment.infrastructure_iam_role_ebs_policy](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource |
+| [aws_iam_role_policy_attachment.infrastructure_iam_role_load_balancer_policy](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource |
| [aws_iam_role_policy_attachment.infrastructure_iam_role_vpc_lattice_policy](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource |
| [aws_iam_role_policy_attachment.service](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource |
| [aws_iam_role_policy_attachment.task_exec](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource |
@@ -251,11 +255,12 @@ module "ecs_service" {
| [create\_task\_exec\_policy](#input\_create\_task\_exec\_policy) | Determines whether the ECS task definition IAM policy should be created. This includes permissions included in AmazonECSTaskExecutionRolePolicy as well as access to secrets and SSM parameters | `bool` | `true` | no |
| [create\_tasks\_iam\_role](#input\_create\_tasks\_iam\_role) | Determines whether the ECS tasks IAM role should be created | `bool` | `true` | no |
| [deployment\_circuit\_breaker](#input\_deployment\_circuit\_breaker) | Configuration block for deployment circuit breaker | object({
enable = bool
rollback = bool
}) | `null` | no |
-| [deployment\_configuration](#input\_deployment\_configuration) | Configuration block for deployment settings | object({
strategy = optional(string)
bake_time_in_minutes = optional(string)
canary_configuration = optional(object({
canary_bake_time_in_minutes = optional(string)
canary_percent = optional(string)
}))
linear_configuration = optional(object({
step_bake_time_in_minutes = optional(string)
step_percent = optional(string)
}))
lifecycle_hook = optional(map(object({
hook_target_arn = string
role_arn = string
lifecycle_stages = list(string)
hook_details = optional(string)
})))
}) | `null` | no |
+| [deployment\_configuration](#input\_deployment\_configuration) | Configuration block for deployment settings | object({
strategy = optional(string)
bake_time_in_minutes = optional(string)
canary_configuration = optional(object({
canary_bake_time_in_minutes = optional(string)
canary_percent = optional(string)
}))
linear_configuration = optional(object({
step_bake_time_in_minutes = optional(string)
step_percent = optional(string)
}))
lifecycle_hook = optional(map(object({
hook_target_arn = string
role_arn = optional(string)
lifecycle_stages = list(string)
hook_details = optional(string)
})))
}) | `null` | no |
| [deployment\_controller](#input\_deployment\_controller) | Configuration block for deployment controller configuration | object({
type = optional(string)
}) | `null` | no |
| [deployment\_maximum\_percent](#input\_deployment\_maximum\_percent) | Upper limit (as a percentage of the service's `desired_count`) of the number of running tasks that can be running in a service during a deployment | `number` | `200` | no |
| [deployment\_minimum\_healthy\_percent](#input\_deployment\_minimum\_healthy\_percent) | Lower limit (as a percentage of the service's `desired_count`) of the number of running tasks that must remain running and healthy in a service during a deployment | `number` | `66` | no |
| [desired\_count](#input\_desired\_count) | Number of instances of the task definition to place and keep running | `number` | `1` | no |
+| [disable\_v7\_default\_name\_description](#input\_disable\_v7\_default\_name\_description) | [DEPRECATED - will be removed in next breaking change] Determines whether to disable the default postfix added to resource names | `bool` | `false` | no |
| [enable\_autoscaling](#input\_enable\_autoscaling) | Determines whether to enable autoscaling for the service | `bool` | `true` | no |
| [enable\_ecs\_managed\_tags](#input\_enable\_ecs\_managed\_tags) | Specifies whether to enable Amazon ECS managed tags for the tasks within the service | `bool` | `true` | no |
| [enable\_execute\_command](#input\_enable\_execute\_command) | Specifies whether to enable Amazon ECS Exec for the tasks within the service | `bool` | `false` | no |
@@ -284,18 +289,18 @@ module "ecs_service" {
| [infrastructure\_iam\_role\_use\_name\_prefix](#input\_infrastructure\_iam\_role\_use\_name\_prefix) | Determines whether the IAM role name (`iam_role_name`) is used as a prefix | `bool` | `true` | no |
| [ipc\_mode](#input\_ipc\_mode) | IPC resource namespace to be used for the containers in the task The valid values are `host`, `task`, and `none` | `string` | `null` | no |
| [launch\_type](#input\_launch\_type) | Launch type on which to run your service. The valid values are `EC2`, `FARGATE`, and `EXTERNAL`. Defaults to `FARGATE` | `string` | `"FARGATE"` | no |
-| [load\_balancer](#input\_load\_balancer) | Configuration block for load balancers | map(object({
container_name = string
container_port = number
elb_name = optional(string)
target_group_arn = optional(string)
advanced_configuration = optional(object({
alternate_target_group_arn = string
production_listener_rule = string
role_arn = string
test_listener_rule = optional(string)
}))
})) | `null` | no |
+| [load\_balancer](#input\_load\_balancer) | Configuration block for load balancers | map(object({
container_name = string
container_port = number
elb_name = optional(string)
target_group_arn = optional(string)
advanced_configuration = optional(object({
alternate_target_group_arn = string
production_listener_rule = string # Should be optional but bug in provider
role_arn = optional(string)
test_listener_rule = optional(string)
}))
})) | `null` | no |
| [memory](#input\_memory) | Amount (in MiB) of memory used by the task. If the `requires_compatibilities` is `FARGATE` this field is required | `number` | `2048` | no |
-| [name](#input\_name) | Name of the service (up to 255 letters, numbers, hyphens, and underscores) | `string` | `null` | no |
+| [name](#input\_name) | Name of the service (up to 255 letters, numbers, hyphens, and underscores) | `string` | `""` | no |
| [network\_mode](#input\_network\_mode) | Docker networking mode to use for the containers in the task. Valid values are `none`, `bridge`, `awsvpc`, and `host` | `string` | `"awsvpc"` | no |
-| [ordered\_placement\_strategy](#input\_ordered\_placement\_strategy) | Service level strategy rules that are taken into consideration during task placement. List from top to bottom in order of precedence | map(object({
field = optional(string)
type = string
})) | `null` | no |
+| [ordered\_placement\_strategy](#input\_ordered\_placement\_strategy) | Service level strategy rules that are taken into consideration during task placement. List from top to bottom in order of precedence | list(object({
field = optional(string)
type = string
})) | `null` | no |
| [pid\_mode](#input\_pid\_mode) | Process namespace to use for the containers in the task. The valid values are `host` and `task` | `string` | `null` | no |
| [placement\_constraints](#input\_placement\_constraints) | Configuration block for rules that are taken into consideration during task placement (up to max of 10). This is set at the service, see `task_definition_placement_constraints` for setting at the task definition | map(object({
expression = optional(string)
type = string
})) | `null` | no |
| [platform\_version](#input\_platform\_version) | Platform version on which to run your service. Only applicable for `launch_type` set to `FARGATE`. Defaults to `LATEST` | `string` | `null` | no |
| [propagate\_tags](#input\_propagate\_tags) | Specifies whether to propagate the tags from the task definition or the service to the tasks. The valid values are `SERVICE` and `TASK_DEFINITION` | `string` | `null` | no |
| [proxy\_configuration](#input\_proxy\_configuration) | Configuration block for the App Mesh proxy | object({
container_name = string
properties = optional(map(string))
type = optional(string)
}) | `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 |
-| [requires\_compatibilities](#input\_requires\_compatibilities) | Set of launch types required by the task. The valid values are `EC2` and `FARGATE` | `list(string)` | [| no | +| [requires\_compatibilities](#input\_requires\_compatibilities) | Set of launch types required by the task. The valid values are `EC2`, `FARGATE`, `EXTERNAL`, and `MANAGED_INSTANCES` | `list(string)` |
"FARGATE"
]
[| no | | [runtime\_platform](#input\_runtime\_platform) | Configuration block for `runtime_platform` that containers in your task may use |
"FARGATE"
]
object({
cpu_architecture = optional(string, "X86_64")
operating_system_family = optional(string, "LINUX")
}) | {
"cpu_architecture": "X86_64",
"operating_system_family": "LINUX"
} | no |
| [scale](#input\_scale) | A floating-point percentage of the desired number of tasks to place and keep running in the task set | object({
unit = optional(string)
value = optional(number)
}) | `null` | no |
| [scheduling\_strategy](#input\_scheduling\_strategy) | Scheduling strategy to use for the service. The valid values are `REPLICA` and `DAEMON`. Defaults to `REPLICA` | `string` | `null` | no |
diff --git a/modules/service/main.tf b/modules/service/main.tf
index b97f7042..a10798f4 100644
--- a/modules/service/main.tf
+++ b/modules/service/main.tf
@@ -60,7 +60,7 @@ resource "aws_ecs_service" "this" {
content {
base = capacity_provider_strategy.value.base
capacity_provider = capacity_provider_strategy.value.capacity_provider
- weight = capacity_provider_strategy.value.weight
+ weight = coalesce(capacity_provider_strategy.value.weight, 1)
}
}
@@ -105,7 +105,7 @@ resource "aws_ecs_service" "this" {
content {
hook_target_arn = lifecycle_hook.value.hook_target_arn
- role_arn = lifecycle_hook.value.role_arn
+ role_arn = try(coalesce(lifecycle_hook.value.role_arn, local.infrastructure_iam_role_arn))
lifecycle_stages = lifecycle_hook.value.lifecycle_stages
hook_details = lifecycle_hook.value.hook_details
}
@@ -148,7 +148,7 @@ resource "aws_ecs_service" "this" {
content {
alternate_target_group_arn = advanced_configuration.value.alternate_target_group_arn
production_listener_rule = advanced_configuration.value.production_listener_rule
- role_arn = advanced_configuration.value.role_arn
+ role_arn = try(coalesce(advanced_configuration.value.role_arn, local.infrastructure_iam_role_arn))
test_listener_rule = advanced_configuration.value.test_listener_rule
}
}
@@ -169,7 +169,7 @@ resource "aws_ecs_service" "this" {
}
dynamic "ordered_placement_strategy" {
- for_each = var.ordered_placement_strategy != null ? var.ordered_placement_strategy : {}
+ for_each = var.ordered_placement_strategy != null ? var.ordered_placement_strategy : []
content {
field = ordered_placement_strategy.value.field
@@ -364,6 +364,8 @@ resource "aws_ecs_service" "this" {
depends_on = [
aws_iam_role_policy_attachment.service,
aws_iam_role_policy_attachment.infrastructure_iam_role_ebs_policy,
+ aws_vpc_security_group_ingress_rule.this,
+ aws_vpc_security_group_egress_rule.this,
]
lifecycle {
@@ -401,7 +403,7 @@ resource "aws_ecs_service" "ignore_task_definition" {
content {
base = capacity_provider_strategy.value.base
capacity_provider = capacity_provider_strategy.value.capacity_provider
- weight = capacity_provider_strategy.value.weight
+ weight = coalesce(capacity_provider_strategy.value.weight, 1)
}
}
@@ -427,8 +429,8 @@ resource "aws_ecs_service" "ignore_task_definition" {
for_each = deployment_configuration.value.linear_configuration != null ? [deployment_configuration.value.linear_configuration] : []
content {
- step_percent = linear_configuration.value.step_percent
step_bake_time_in_minutes = linear_configuration.value.step_bake_time_in_minutes
+ step_percent = linear_configuration.value.step_percent
}
}
@@ -436,8 +438,8 @@ resource "aws_ecs_service" "ignore_task_definition" {
for_each = deployment_configuration.value.canary_configuration != null ? [deployment_configuration.value.canary_configuration] : []
content {
- canary_percent = canary_configuration.value.canary_percent
canary_bake_time_in_minutes = canary_configuration.value.canary_bake_time_in_minutes
+ canary_percent = canary_configuration.value.canary_percent
}
}
@@ -446,7 +448,7 @@ resource "aws_ecs_service" "ignore_task_definition" {
content {
hook_target_arn = lifecycle_hook.value.hook_target_arn
- role_arn = lifecycle_hook.value.role_arn
+ role_arn = try(coalesce(lifecycle_hook.value.role_arn, local.infrastructure_iam_role_arn))
lifecycle_stages = lifecycle_hook.value.lifecycle_stages
hook_details = lifecycle_hook.value.hook_details
}
@@ -489,7 +491,7 @@ resource "aws_ecs_service" "ignore_task_definition" {
content {
alternate_target_group_arn = advanced_configuration.value.alternate_target_group_arn
production_listener_rule = advanced_configuration.value.production_listener_rule
- role_arn = advanced_configuration.value.role_arn
+ role_arn = try(coalesce(advanced_configuration.value.role_arn, local.infrastructure_iam_role_arn))
test_listener_rule = advanced_configuration.value.test_listener_rule
}
}
@@ -510,7 +512,7 @@ resource "aws_ecs_service" "ignore_task_definition" {
}
dynamic "ordered_placement_strategy" {
- for_each = var.ordered_placement_strategy != null ? var.ordered_placement_strategy : {}
+ for_each = var.ordered_placement_strategy != null ? var.ordered_placement_strategy : []
content {
field = ordered_placement_strategy.value.field
@@ -639,6 +641,8 @@ resource "aws_ecs_service" "ignore_task_definition" {
}
}
+ sigint_rollback = try(var.deployment_configuration.strategy, null) == "BLUE_GREEN" ? var.sigint_rollback : null
+
tags = merge(var.tags, var.service_tags)
task_definition = local.task_definition
triggers = var.triggers
@@ -647,7 +651,7 @@ resource "aws_ecs_service" "ignore_task_definition" {
for_each = var.volume_configuration != null ? [var.volume_configuration] : []
content {
- name = volume_configuration.value.name
+ name = try(volume_configuration.value.name, volume_configuration.key)
dynamic "managed_ebs_volume" {
for_each = [volume_configuration.value.managed_ebs_volume]
@@ -703,6 +707,8 @@ resource "aws_ecs_service" "ignore_task_definition" {
depends_on = [
aws_iam_role_policy_attachment.service,
aws_iam_role_policy_attachment.infrastructure_iam_role_ebs_policy,
+ aws_vpc_security_group_ingress_rule.this,
+ aws_vpc_security_group_egress_rule.this,
]
lifecycle {
@@ -747,7 +753,7 @@ resource "aws_iam_role" "service" {
name = var.iam_role_use_name_prefix ? null : local.iam_role_name
name_prefix = var.iam_role_use_name_prefix ? "${local.iam_role_name}-" : null
path = var.iam_role_path
- description = var.iam_role_description
+ description = try(coalesce(var.iam_role_description, (var.disable_v7_default_name_description ? null : "IAM role for ECS Service ${var.name}")), null)
assume_role_policy = data.aws_iam_policy_document.service_assume[0].json
permissions_boundary = var.iam_role_permissions_boundary
@@ -1053,7 +1059,7 @@ resource "aws_ecs_task_definition" "this" {
################################################################################
locals {
- task_exec_iam_role_name = coalesce(var.task_exec_iam_role_name, var.name, "NotProvided")
+ task_exec_iam_role_name = coalesce(var.task_exec_iam_role_name, "${var.name}${var.disable_v7_default_name_description ? "" : "-task-exec"}")
create_task_exec_iam_role = local.create_task_definition && var.create_task_exec_iam_role
create_task_exec_policy = local.create_task_exec_iam_role && var.create_task_exec_policy
@@ -1207,7 +1213,7 @@ resource "aws_iam_role_policy_attachment" "task_exec" {
################################################################################
locals {
- tasks_iam_role_name = coalesce(var.tasks_iam_role_name, var.name, "NotProvided")
+ tasks_iam_role_name = coalesce(var.tasks_iam_role_name, "${var.name}${var.disable_v7_default_name_description ? "" : "-tasks"}")
create_tasks_iam_role = local.create_task_definition && var.create_tasks_iam_role
}
@@ -1244,7 +1250,7 @@ resource "aws_iam_role" "tasks" {
name = var.tasks_iam_role_use_name_prefix ? null : local.tasks_iam_role_name
name_prefix = var.tasks_iam_role_use_name_prefix ? "${local.tasks_iam_role_name}-" : null
path = var.tasks_iam_role_path
- description = var.tasks_iam_role_description
+ description = try(coalesce(var.tasks_iam_role_description, (var.disable_v7_default_name_description ? null : "IAM role for ECS tasks in Service ${var.name}")), null)
assume_role_policy = data.aws_iam_policy_document.tasks_assume[0].json
permissions_boundary = var.tasks_iam_role_permissions_boundary
@@ -1393,7 +1399,7 @@ resource "aws_ecs_task_set" "this" {
content {
base = capacity_provider_strategy.value.base
capacity_provider = capacity_provider_strategy.value.capacity_provider
- weight = capacity_provider_strategy.value.weight
+ weight = coalesce(capacity_provider_strategy.value.weight, 1)
}
}
@@ -1476,7 +1482,7 @@ resource "aws_ecs_task_set" "ignore_task_definition" {
content {
base = capacity_provider_strategy.value.base
capacity_provider = capacity_provider_strategy.value.capacity_provider
- weight = capacity_provider_strategy.value.weight
+ weight = coalesce(capacity_provider_strategy.value.weight, 1)
}
}
@@ -1864,7 +1870,7 @@ resource "aws_appautoscaling_scheduled_action" "this" {
locals {
create_security_group = var.create && var.create_security_group && var.network_mode == "awsvpc"
- security_group_name = coalesce(var.security_group_name, var.name, "NotProvided")
+ security_group_name = coalesce(var.security_group_name, "${var.name}${var.disable_v7_default_name_description ? "" : "-service"}")
}
data "aws_subnet" "this" {
@@ -1882,7 +1888,7 @@ resource "aws_security_group" "this" {
name = var.security_group_use_name_prefix ? null : local.security_group_name
name_prefix = var.security_group_use_name_prefix ? "${local.security_group_name}-" : null
- description = var.security_group_description
+ description = try(coalesce(var.security_group_description, (var.disable_v7_default_name_description ? null : "Security group for ECS Service ${var.name}")), null)
vpc_id = var.vpc_id != null ? var.vpc_id : data.aws_subnet.this[0].vpc_id
tags = merge(
@@ -1907,7 +1913,7 @@ resource "aws_vpc_security_group_ingress_rule" "this" {
from_port = each.value.from_port
ip_protocol = each.value.ip_protocol
prefix_list_id = each.value.prefix_list_id
- referenced_security_group_id = each.value.referenced_security_group_id
+ referenced_security_group_id = each.value.referenced_security_group_id == "self" ? aws_security_group.this[0].id : each.value.referenced_security_group_id
security_group_id = aws_security_group.this[0].id
tags = merge(
var.tags,
@@ -1929,7 +1935,7 @@ resource "aws_vpc_security_group_egress_rule" "this" {
from_port = try(coalesce(each.value.from_port, each.value.to_port), null)
ip_protocol = each.value.ip_protocol
prefix_list_id = each.value.prefix_list_id
- referenced_security_group_id = each.value.referenced_security_group_id
+ referenced_security_group_id = each.value.referenced_security_group_id == "self" ? aws_security_group.this[0].id : each.value.referenced_security_group_id
security_group_id = aws_security_group.this[0].id
tags = merge(
var.tags,
@@ -1946,10 +1952,10 @@ resource "aws_vpc_security_group_egress_rule" "this" {
############################################################################################
locals {
- needs_infrastructure_iam_role = var.volume_configuration != null || var.vpc_lattice_configurations != null
+ needs_infrastructure_iam_role = var.volume_configuration != null || var.vpc_lattice_configurations != null || var.deployment_configuration != null
create_infrastructure_iam_role = var.create && var.create_infrastructure_iam_role && local.needs_infrastructure_iam_role
infrastructure_iam_role_arn = local.needs_infrastructure_iam_role ? try(aws_iam_role.infrastructure_iam_role[0].arn, var.infrastructure_iam_role_arn) : null
- infrastructure_iam_role_name = coalesce(var.infrastructure_iam_role_name, var.name, "NotProvided")
+ infrastructure_iam_role_name = coalesce(var.infrastructure_iam_role_name, "${var.name}${var.disable_v7_default_name_description ? "" : "-infra"}")
}
data "aws_iam_policy_document" "infrastructure_iam_role" {
@@ -1994,3 +2000,10 @@ resource "aws_iam_role_policy_attachment" "infrastructure_iam_role_vpc_lattice_p
role = aws_iam_role.infrastructure_iam_role[0].name
policy_arn = "arn:${local.partition}:iam::aws:policy/service-role/AmazonECSInfrastructureRolePolicyForVpcLattice"
}
+
+resource "aws_iam_role_policy_attachment" "infrastructure_iam_role_load_balancer_policy" {
+ count = local.create_infrastructure_iam_role && var.deployment_configuration != null ? 1 : 0
+
+ role = aws_iam_role.infrastructure_iam_role[0].name
+ policy_arn = "arn:${local.partition}:iam::aws:policy/AmazonECSInfrastructureRolePolicyForLoadBalancers"
+}
diff --git a/modules/service/variables.tf b/modules/service/variables.tf
index b42dfe40..2f711ed8 100644
--- a/modules/service/variables.tf
+++ b/modules/service/variables.tf
@@ -25,6 +25,12 @@ variable "tags" {
nullable = false
}
+variable "disable_v7_default_name_description" {
+ description = "[DEPRECATED - will be removed in next breaking change] Determines whether to disable the default postfix added to resource names"
+ type = bool
+ default = false
+}
+
################################################################################
# Service
################################################################################
@@ -93,7 +99,7 @@ variable "deployment_configuration" {
}))
lifecycle_hook = optional(map(object({
hook_target_arn = string
- role_arn = string
+ role_arn = optional(string)
lifecycle_stages = list(string)
hook_details = optional(string)
})))
@@ -176,8 +182,8 @@ variable "load_balancer" {
target_group_arn = optional(string)
advanced_configuration = optional(object({
alternate_target_group_arn = string
- production_listener_rule = string
- role_arn = string
+ production_listener_rule = string # Should be optional but bug in provider
+ role_arn = optional(string)
test_listener_rule = optional(string)
}))
}))
@@ -187,7 +193,7 @@ variable "load_balancer" {
variable "name" {
description = "Name of the service (up to 255 letters, numbers, hyphens, and underscores)"
type = string
- default = null
+ default = ""
}
variable "assign_public_ip" {
@@ -225,7 +231,7 @@ variable "vpc_id" {
variable "ordered_placement_strategy" {
description = "Service level strategy rules that are taken into consideration during task placement. List from top to bottom in order of precedence"
- type = map(object({
+ type = list(object({
field = optional(string)
type = string
}))
@@ -683,7 +689,7 @@ variable "proxy_configuration" {
}
variable "requires_compatibilities" {
- description = "Set of launch types required by the task. The valid values are `EC2` and `FARGATE`"
+ description = "Set of launch types required by the task. The valid values are `EC2`, `FARGATE`, `EXTERNAL`, and `MANAGED_INSTANCES`"
type = list(string)
default = ["FARGATE"]
nullable = false
@@ -1292,7 +1298,7 @@ variable "security_group_tags" {
}
############################################################################################
-# ECS Infrastructure IAM role
+# Infrastructure IAM role
############################################################################################
variable "create_infrastructure_iam_role" {
diff --git a/modules/service/versions.tf b/modules/service/versions.tf
index 70f23a44..7e5b918f 100644
--- a/modules/service/versions.tf
+++ b/modules/service/versions.tf
@@ -4,7 +4,7 @@ terraform {
required_providers {
aws = {
source = "hashicorp/aws"
- version = ">= 6.21"
+ version = ">= 6.23"
}
}
}
diff --git a/outputs.tf b/outputs.tf
index 6e462c6e..ecb6f90d 100644
--- a/outputs.tf
+++ b/outputs.tf
@@ -32,9 +32,9 @@ output "cluster_capacity_providers" {
value = module.cluster.cluster_capacity_providers
}
-output "autoscaling_capacity_providers" {
+output "capacity_providers" {
description = "Map of autoscaling capacity providers created and their attributes"
- value = module.cluster.autoscaling_capacity_providers
+ value = module.cluster.capacity_providers
}
output "task_exec_iam_role_name" {
@@ -52,6 +52,51 @@ output "task_exec_iam_role_unique_id" {
value = module.cluster.task_exec_iam_role_unique_id
}
+output "infrastructure_iam_role_arn" {
+ description = "The Amazon Resource Name (ARN) specifying the IAM role"
+ value = module.cluster.infrastructure_iam_role_arn
+}
+
+output "infrastructure_iam_role_name" {
+ description = "IAM role name"
+ value = module.cluster.infrastructure_iam_role_name
+}
+
+output "infrastructure_iam_role_unique_id" {
+ description = "Stable and unique string identifying the IAM role"
+ value = module.cluster.infrastructure_iam_role_unique_id
+}
+
+output "node_iam_role_arn" {
+ description = "The Amazon Resource Name (ARN) specifying the IAM role"
+ value = module.cluster.node_iam_role_arn
+}
+
+output "node_iam_role_name" {
+ description = "IAM role name"
+ value = module.cluster.node_iam_role_name
+}
+
+output "node_iam_role_unique_id" {
+ description = "Stable and unique string identifying the IAM role"
+ value = module.cluster.node_iam_role_unique_id
+}
+
+output "node_iam_instance_profile_arn" {
+ description = "ARN assigned by AWS to the instance profile"
+ value = module.cluster.node_iam_instance_profile_arn
+}
+
+output "node_iam_instance_profile_id" {
+ description = "Instance profile's ID"
+ value = module.cluster.node_iam_instance_profile_id
+}
+
+output "node_iam_instance_profile_unique" {
+ description = "Stable and unique string identifying the IAM instance profile"
+ value = module.cluster.node_iam_instance_profile_unique
+}
+
################################################################################
# Service(s)
################################################################################
diff --git a/variables.tf b/variables.tf
index 5c354d6f..76b6e4e7 100644
--- a/variables.tf
+++ b/variables.tf
@@ -16,6 +16,12 @@ variable "tags" {
default = {}
}
+variable "disable_v7_default_name_description" {
+ description = "[DEPRECATED - will be removed in v9.0] Determines whether to disable the default postfix added to resource names and descriptions added in v7.0"
+ type = bool
+ default = false
+}
+
################################################################################
# Cluster
################################################################################
@@ -126,21 +132,97 @@ variable "cloudwatch_log_group_tags" {
# Capacity Providers
################################################################################
-variable "autoscaling_capacity_providers" {
- description = "Map of autoscaling capacity provider definitions to create for the cluster"
+variable "cluster_capacity_providers" {
+ description = "List of capacity provider names to associate with the ECS cluster. Note: any capacity providers created by this module will be automatically added"
+ type = list(string)
+ default = []
+}
+
+variable "capacity_providers" {
+ description = "Map of capacity provider definitions to create for the cluster"
type = map(object({
- auto_scaling_group_arn = string
- managed_draining = optional(string, "ENABLED")
- managed_scaling = optional(object({
- instance_warmup_period = optional(number)
- maximum_scaling_step_size = optional(number)
- minimum_scaling_step_size = optional(number)
- status = optional(string)
- target_capacity = optional(number)
+ auto_scaling_group_provider = optional(object({
+ auto_scaling_group_arn = string
+ managed_draining = optional(string, "ENABLED")
+ managed_scaling = optional(object({
+ instance_warmup_period = optional(number)
+ maximum_scaling_step_size = optional(number)
+ minimum_scaling_step_size = optional(number)
+ status = optional(string)
+ target_capacity = optional(number)
+ }))
+ managed_termination_protection = optional(string)
+ }))
+ managed_instances_provider = optional(object({
+ infrastructure_role_arn = optional(string)
+ instance_launch_template = object({
+ ec2_instance_profile_arn = optional(string)
+ instance_requirements = optional(object({
+ accelerator_count = optional(object({
+ max = optional(number)
+ min = optional(number)
+ }))
+ accelerator_manufacturers = optional(list(string))
+ accelerator_names = optional(list(string))
+ accelerator_total_memory_mib = optional(object({
+ max = optional(number)
+ min = optional(number)
+ }))
+ accelerator_types = optional(list(string))
+ allowed_instance_types = optional(list(string))
+ bare_metal = optional(string)
+ baseline_ebs_bandwidth_mbps = optional(object({
+ max = optional(number)
+ min = optional(number)
+ }))
+ burstable_performance = optional(string)
+ cpu_manufacturers = optional(list(string))
+ excluded_instance_types = optional(list(string))
+ instance_generations = optional(list(string))
+ local_storage = optional(string)
+ local_storage_types = optional(list(string))
+ max_spot_price_as_percentage_of_optimal_on_demand_price = optional(number)
+ memory_gib_per_vcpu = optional(object({
+ max = optional(number)
+ min = optional(number)
+ }))
+ memory_mib = optional(object({
+ max = optional(number)
+ min = optional(number)
+ }))
+ network_bandwidth_gbps = optional(object({
+ max = optional(number)
+ min = optional(number)
+ }))
+ network_interface_count = optional(object({
+ max = optional(number)
+ min = optional(number)
+ }))
+ on_demand_max_price_percentage_over_lowest_price = optional(number)
+ require_hibernate_support = optional(bool)
+ spot_max_price_percentage_over_lowest_price = optional(number)
+ total_local_storage_gb = optional(object({
+ max = optional(number)
+ min = optional(number)
+ }))
+ vcpu_count = optional(object({
+ max = optional(number)
+ min = optional(number)
+ }))
+ }))
+ monitoring = optional(string)
+ network_configuration = optional(object({
+ security_groups = optional(list(string), [])
+ subnets = list(string)
+ }))
+ storage_configuration = optional(object({
+ storage_size_gib = number
+ }))
+ })
+ propagate_tags = optional(string, "CAPACITY_PROVIDER")
}))
- managed_termination_protection = optional(string)
- name = optional(string) # Will fall back to use map key if not set
- tags = optional(map(string), {})
+ name = optional(string) # Will fall back to use map key if not set
+ tags = optional(map(string), {})
}))
default = null
}
@@ -252,6 +334,288 @@ variable "task_exec_iam_statements" {
default = null
}
+############################################################################################
+# Infrastructure IAM role
+############################################################################################
+
+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_name" {
+ description = "Name to use on IAM role created"
+ type = string
+ default = null
+}
+
+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" {
+ description = "IAM role path"
+ type = string
+ default = null
+}
+
+variable "infrastructure_iam_role_description" {
+ description = "Description of the role"
+ type = string
+ default = null
+}
+
+variable "infrastructure_iam_role_permissions_boundary" {
+ description = "ARN of the policy that is used to set the permissions boundary for the IAM role"
+ type = string
+ default = null
+}
+
+variable "infrastructure_iam_role_tags" {
+ description = "A map of additional tags to add to the IAM role created"
+ type = map(string)
+ default = {}
+ nullable = false
+}
+
+################################################################################
+# Infrastructure IAM role policy
+################################################################################
+
+variable "infrastructure_iam_role_source_policy_documents" {
+ description = "List of IAM policy documents that are merged together into the exported document. Statements must have unique `sid`s"
+ type = list(string)
+ default = []
+}
+
+variable "infrastructure_iam_role_override_policy_documents" {
+ description = "List of IAM policy documents that are merged together into the exported document. In merging, statements with non-blank `sid`s will override statements with the same `sid`"
+ type = list(string)
+ default = []
+}
+
+variable "infrastructure_iam_role_statements" {
+ description = "A map of IAM policy [statements](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document#statement) for custom permission usage"
+ type = map(object({
+ sid = optional(string)
+ actions = optional(list(string))
+ not_actions = optional(list(string))
+ effect = optional(string, "Allow")
+ 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
+ variable = string
+ values = list(string)
+ })))
+ }))
+ default = null
+}
+
+################################################################################
+# Node IAM role & instance profile
+################################################################################
+
+variable "create_node_iam_instance_profile" {
+ description = "Determines whether an IAM instance profile is created or to use an existing IAM instance profile"
+ type = bool
+ default = true
+ nullable = false
+}
+
+variable "node_iam_role_name" {
+ description = "Name to use on IAM role/instance profile created"
+ type = string
+ default = null
+}
+
+variable "node_iam_role_use_name_prefix" {
+ description = "Determines whether the IAM role/instance profile name (`node_iam_role_name`) is used as a prefix"
+ type = bool
+ default = true
+ nullable = false
+}
+
+variable "node_iam_role_path" {
+ description = "IAM role/instance profile path"
+ type = string
+ default = null
+}
+
+variable "node_iam_role_description" {
+ description = "Description of the role"
+ type = string
+ default = "ECS Managed Instances node IAM role"
+ nullable = false
+}
+
+variable "node_iam_role_permissions_boundary" {
+ description = "ARN of the policy that is used to set the permissions boundary for the IAM role"
+ type = string
+ default = null
+}
+
+variable "node_iam_role_additional_policies" {
+ description = "Additional policies to be added to the IAM role"
+ type = map(string)
+ default = {}
+ nullable = false
+}
+
+variable "node_iam_role_tags" {
+ description = "A map of additional tags to add to the IAM role/instance profile created"
+ type = map(string)
+ default = {}
+ nullable = false
+}
+
+################################################################################
+# Node IAM role policy
+################################################################################
+
+variable "node_iam_role_source_policy_documents" {
+ description = "List of IAM policy documents that are merged together into the exported document. Statements must have unique `sid`s"
+ type = list(string)
+ default = []
+}
+
+variable "node_iam_role_override_policy_documents" {
+ description = "List of IAM policy documents that are merged together into the exported document. In merging, statements with non-blank `sid`s will override statements with the same `sid`"
+ type = list(string)
+ default = []
+}
+
+variable "node_iam_role_statements" {
+ description = "A map of IAM policy [statements](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document#statement) for custom permission usage"
+ type = map(object({
+ sid = optional(string)
+ actions = optional(list(string))
+ not_actions = optional(list(string))
+ effect = optional(string, "Allow")
+ 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
+ variable = string
+ values = list(string)
+ })))
+ }))
+ default = null
+}
+
+################################################################################
+# Security Group
+################################################################################
+
+variable "create_security_group" {
+ description = "Determines if a security group is created"
+ type = bool
+ default = true
+ nullable = false
+}
+
+variable "vpc_id" {
+ description = "The ID of the VPC where the security group will be created"
+ type = string
+ default = null
+}
+
+variable "security_group_name" {
+ description = "Name to use on security group created"
+ type = string
+ default = null
+}
+
+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" {
+ description = "Description of the security group created"
+ type = string
+ default = null
+}
+
+variable "security_group_ingress_rules" {
+ description = "Security group ingress rules to add to the security group created"
+ type = map(object({
+ name = optional(string)
+
+ 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 = {}
+ nullable = false
+}
+
+variable "security_group_egress_rules" {
+ description = "Security group egress rules to add to the security group created"
+ type = map(object({
+ name = optional(string)
+
+ 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 = {
+ all_ipv4 = {
+ cidr_ipv4 = "0.0.0.0/0"
+ description = "Allow all IPv4 traffic"
+ ip_protocol = "-1"
+ }
+ all_ipv6 = {
+ cidr_ipv6 = "::/0"
+ description = "Allow all IPv6 traffic"
+ ip_protocol = "-1"
+ }
+ }
+ 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
+}
+
################################################################################
# Service(s)
################################################################################
@@ -293,7 +657,7 @@ variable "services" {
}))
lifecycle_hook = optional(map(object({
hook_target_arn = string
- role_arn = string
+ role_arn = optional(string)
lifecycle_stages = list(string)
hook_details = optional(string)
})))
@@ -317,8 +681,8 @@ variable "services" {
target_group_arn = optional(string)
advanced_configuration = optional(object({
alternate_target_group_arn = string
- production_listener_rule = string
- role_arn = string
+ production_listener_rule = string # Should be optional but bug in provider
+ role_arn = optional(string)
test_listener_rule = optional(string)
}))
})))
@@ -326,7 +690,7 @@ variable "services" {
assign_public_ip = optional(bool)
security_group_ids = optional(list(string))
subnet_ids = optional(list(string))
- ordered_placement_strategy = optional(map(object({
+ ordered_placement_strategy = optional(list(object({
field = optional(string)
type = string
})))
diff --git a/versions.tf b/versions.tf
index 70f23a44..7e5b918f 100644
--- a/versions.tf
+++ b/versions.tf
@@ -4,7 +4,7 @@ terraform {
required_providers {
aws = {
source = "hashicorp/aws"
- version = ">= 6.21"
+ version = ">= 6.23"
}
}
}
diff --git a/wrappers/cluster/main.tf b/wrappers/cluster/main.tf
index bbd27c5d..40f33ef5 100644
--- a/wrappers/cluster/main.tf
+++ b/wrappers/cluster/main.tf
@@ -3,12 +3,14 @@ module "wrapper" {
for_each = var.items
- autoscaling_capacity_providers = try(each.value.autoscaling_capacity_providers, var.defaults.autoscaling_capacity_providers, null)
- cloudwatch_log_group_class = try(each.value.cloudwatch_log_group_class, var.defaults.cloudwatch_log_group_class, null)
- cloudwatch_log_group_kms_key_id = try(each.value.cloudwatch_log_group_kms_key_id, var.defaults.cloudwatch_log_group_kms_key_id, null)
- cloudwatch_log_group_name = try(each.value.cloudwatch_log_group_name, var.defaults.cloudwatch_log_group_name, null)
- cloudwatch_log_group_retention_in_days = try(each.value.cloudwatch_log_group_retention_in_days, var.defaults.cloudwatch_log_group_retention_in_days, 90)
- cloudwatch_log_group_tags = try(each.value.cloudwatch_log_group_tags, var.defaults.cloudwatch_log_group_tags, {})
+ capacity_providers = try(each.value.capacity_providers, var.defaults.capacity_providers, null)
+ cloudwatch_log_group_class = try(each.value.cloudwatch_log_group_class, var.defaults.cloudwatch_log_group_class, null)
+ cloudwatch_log_group_kms_key_id = try(each.value.cloudwatch_log_group_kms_key_id, var.defaults.cloudwatch_log_group_kms_key_id, null)
+ cloudwatch_log_group_name = try(each.value.cloudwatch_log_group_name, var.defaults.cloudwatch_log_group_name, null)
+ cloudwatch_log_group_retention_in_days = try(each.value.cloudwatch_log_group_retention_in_days, var.defaults.cloudwatch_log_group_retention_in_days, 90)
+ cloudwatch_log_group_tags = try(each.value.cloudwatch_log_group_tags, var.defaults.cloudwatch_log_group_tags, {})
+ cluster_capacity_providers = try(each.value.cluster_capacity_providers, var.defaults.cluster_capacity_providers, [])
+ cluster_capacity_providers_wait_duration = try(each.value.cluster_capacity_providers_wait_duration, var.defaults.cluster_capacity_providers_wait_duration, "20s")
configuration = try(each.value.configuration, var.defaults.configuration, {
execute_command_configuration = {
log_configuration = {
@@ -16,14 +18,54 @@ module "wrapper" {
}
}
})
- create = try(each.value.create, var.defaults.create, true)
- create_cloudwatch_log_group = try(each.value.create_cloudwatch_log_group, var.defaults.create_cloudwatch_log_group, true)
- create_task_exec_iam_role = try(each.value.create_task_exec_iam_role, var.defaults.create_task_exec_iam_role, false)
- create_task_exec_policy = try(each.value.create_task_exec_policy, var.defaults.create_task_exec_policy, true)
- default_capacity_provider_strategy = try(each.value.default_capacity_provider_strategy, var.defaults.default_capacity_provider_strategy, {})
- name = try(each.value.name, var.defaults.name, "")
- region = try(each.value.region, var.defaults.region, null)
- service_connect_defaults = try(each.value.service_connect_defaults, var.defaults.service_connect_defaults, null)
+ create = try(each.value.create, var.defaults.create, true)
+ create_cloudwatch_log_group = try(each.value.create_cloudwatch_log_group, var.defaults.create_cloudwatch_log_group, true)
+ create_infrastructure_iam_role = try(each.value.create_infrastructure_iam_role, var.defaults.create_infrastructure_iam_role, true)
+ create_node_iam_instance_profile = try(each.value.create_node_iam_instance_profile, var.defaults.create_node_iam_instance_profile, true)
+ create_security_group = try(each.value.create_security_group, var.defaults.create_security_group, true)
+ create_task_exec_iam_role = try(each.value.create_task_exec_iam_role, var.defaults.create_task_exec_iam_role, false)
+ create_task_exec_policy = try(each.value.create_task_exec_policy, var.defaults.create_task_exec_policy, true)
+ default_capacity_provider_strategy = try(each.value.default_capacity_provider_strategy, var.defaults.default_capacity_provider_strategy, {})
+ disable_v7_default_name_description = try(each.value.disable_v7_default_name_description, var.defaults.disable_v7_default_name_description, false)
+ infrastructure_iam_role_description = try(each.value.infrastructure_iam_role_description, var.defaults.infrastructure_iam_role_description, null)
+ infrastructure_iam_role_name = try(each.value.infrastructure_iam_role_name, var.defaults.infrastructure_iam_role_name, null)
+ infrastructure_iam_role_override_policy_documents = try(each.value.infrastructure_iam_role_override_policy_documents, var.defaults.infrastructure_iam_role_override_policy_documents, [])
+ infrastructure_iam_role_path = try(each.value.infrastructure_iam_role_path, var.defaults.infrastructure_iam_role_path, null)
+ infrastructure_iam_role_permissions_boundary = try(each.value.infrastructure_iam_role_permissions_boundary, var.defaults.infrastructure_iam_role_permissions_boundary, null)
+ infrastructure_iam_role_source_policy_documents = try(each.value.infrastructure_iam_role_source_policy_documents, var.defaults.infrastructure_iam_role_source_policy_documents, [])
+ infrastructure_iam_role_statements = try(each.value.infrastructure_iam_role_statements, var.defaults.infrastructure_iam_role_statements, null)
+ infrastructure_iam_role_tags = try(each.value.infrastructure_iam_role_tags, var.defaults.infrastructure_iam_role_tags, {})
+ infrastructure_iam_role_use_name_prefix = try(each.value.infrastructure_iam_role_use_name_prefix, var.defaults.infrastructure_iam_role_use_name_prefix, true)
+ name = try(each.value.name, var.defaults.name, "")
+ node_iam_role_additional_policies = try(each.value.node_iam_role_additional_policies, var.defaults.node_iam_role_additional_policies, {})
+ node_iam_role_description = try(each.value.node_iam_role_description, var.defaults.node_iam_role_description, "ECS Managed Instances node IAM role")
+ node_iam_role_name = try(each.value.node_iam_role_name, var.defaults.node_iam_role_name, null)
+ node_iam_role_override_policy_documents = try(each.value.node_iam_role_override_policy_documents, var.defaults.node_iam_role_override_policy_documents, [])
+ node_iam_role_path = try(each.value.node_iam_role_path, var.defaults.node_iam_role_path, null)
+ node_iam_role_permissions_boundary = try(each.value.node_iam_role_permissions_boundary, var.defaults.node_iam_role_permissions_boundary, null)
+ node_iam_role_source_policy_documents = try(each.value.node_iam_role_source_policy_documents, var.defaults.node_iam_role_source_policy_documents, [])
+ node_iam_role_statements = try(each.value.node_iam_role_statements, var.defaults.node_iam_role_statements, null)
+ node_iam_role_tags = try(each.value.node_iam_role_tags, var.defaults.node_iam_role_tags, {})
+ node_iam_role_use_name_prefix = try(each.value.node_iam_role_use_name_prefix, var.defaults.node_iam_role_use_name_prefix, true)
+ region = try(each.value.region, var.defaults.region, null)
+ security_group_description = try(each.value.security_group_description, var.defaults.security_group_description, null)
+ security_group_egress_rules = try(each.value.security_group_egress_rules, var.defaults.security_group_egress_rules, {
+ all_ipv4 = {
+ cidr_ipv4 = "0.0.0.0/0"
+ description = "Allow all IPv4 traffic"
+ ip_protocol = "-1"
+ }
+ all_ipv6 = {
+ cidr_ipv6 = "::/0"
+ description = "Allow all IPv6 traffic"
+ ip_protocol = "-1"
+ }
+ })
+ security_group_ingress_rules = try(each.value.security_group_ingress_rules, var.defaults.security_group_ingress_rules, {})
+ security_group_name = try(each.value.security_group_name, var.defaults.security_group_name, null)
+ security_group_tags = try(each.value.security_group_tags, var.defaults.security_group_tags, {})
+ security_group_use_name_prefix = try(each.value.security_group_use_name_prefix, var.defaults.security_group_use_name_prefix, true)
+ service_connect_defaults = try(each.value.service_connect_defaults, var.defaults.service_connect_defaults, null)
setting = try(each.value.setting, var.defaults.setting, [
{
name = "containerInsights"
@@ -41,4 +83,5 @@ module "wrapper" {
task_exec_iam_statements = try(each.value.task_exec_iam_statements, var.defaults.task_exec_iam_statements, null)
task_exec_secret_arns = try(each.value.task_exec_secret_arns, var.defaults.task_exec_secret_arns, [])
task_exec_ssm_param_arns = try(each.value.task_exec_ssm_param_arns, var.defaults.task_exec_ssm_param_arns, [])
+ vpc_id = try(each.value.vpc_id, var.defaults.vpc_id, null)
}
diff --git a/wrappers/cluster/versions.tf b/wrappers/cluster/versions.tf
index 70f23a44..bceb79a7 100644
--- a/wrappers/cluster/versions.tf
+++ b/wrappers/cluster/versions.tf
@@ -4,7 +4,11 @@ terraform {
required_providers {
aws = {
source = "hashicorp/aws"
- version = ">= 6.21"
+ version = ">= 6.23"
+ }
+ time = {
+ source = "hashicorp/time"
+ version = ">= 0.13"
}
}
}
diff --git a/wrappers/container-definition/versions.tf b/wrappers/container-definition/versions.tf
index 70f23a44..7e5b918f 100644
--- a/wrappers/container-definition/versions.tf
+++ b/wrappers/container-definition/versions.tf
@@ -4,7 +4,7 @@ terraform {
required_providers {
aws = {
source = "hashicorp/aws"
- version = ">= 6.21"
+ version = ">= 6.23"
}
}
}
diff --git a/wrappers/main.tf b/wrappers/main.tf
index 13659ca0..5068926a 100644
--- a/wrappers/main.tf
+++ b/wrappers/main.tf
@@ -3,12 +3,13 @@ module "wrapper" {
for_each = var.items
- autoscaling_capacity_providers = try(each.value.autoscaling_capacity_providers, var.defaults.autoscaling_capacity_providers, null)
+ capacity_providers = try(each.value.capacity_providers, var.defaults.capacity_providers, null)
cloudwatch_log_group_class = try(each.value.cloudwatch_log_group_class, var.defaults.cloudwatch_log_group_class, null)
cloudwatch_log_group_kms_key_id = try(each.value.cloudwatch_log_group_kms_key_id, var.defaults.cloudwatch_log_group_kms_key_id, null)
cloudwatch_log_group_name = try(each.value.cloudwatch_log_group_name, var.defaults.cloudwatch_log_group_name, null)
cloudwatch_log_group_retention_in_days = try(each.value.cloudwatch_log_group_retention_in_days, var.defaults.cloudwatch_log_group_retention_in_days, 90)
cloudwatch_log_group_tags = try(each.value.cloudwatch_log_group_tags, var.defaults.cloudwatch_log_group_tags, {})
+ cluster_capacity_providers = try(each.value.cluster_capacity_providers, var.defaults.cluster_capacity_providers, [])
cluster_configuration = try(each.value.cluster_configuration, var.defaults.cluster_configuration, {
execute_command_configuration = {
log_configuration = {
@@ -24,13 +25,53 @@ module "wrapper" {
value = "enabled"
}
])
- cluster_tags = try(each.value.cluster_tags, var.defaults.cluster_tags, {})
- create = try(each.value.create, var.defaults.create, true)
- create_cloudwatch_log_group = try(each.value.create_cloudwatch_log_group, var.defaults.create_cloudwatch_log_group, true)
- create_task_exec_iam_role = try(each.value.create_task_exec_iam_role, var.defaults.create_task_exec_iam_role, false)
- create_task_exec_policy = try(each.value.create_task_exec_policy, var.defaults.create_task_exec_policy, true)
- default_capacity_provider_strategy = try(each.value.default_capacity_provider_strategy, var.defaults.default_capacity_provider_strategy, null)
- region = try(each.value.region, var.defaults.region, null)
+ cluster_tags = try(each.value.cluster_tags, var.defaults.cluster_tags, {})
+ create = try(each.value.create, var.defaults.create, true)
+ create_cloudwatch_log_group = try(each.value.create_cloudwatch_log_group, var.defaults.create_cloudwatch_log_group, true)
+ create_infrastructure_iam_role = try(each.value.create_infrastructure_iam_role, var.defaults.create_infrastructure_iam_role, true)
+ create_node_iam_instance_profile = try(each.value.create_node_iam_instance_profile, var.defaults.create_node_iam_instance_profile, true)
+ create_security_group = try(each.value.create_security_group, var.defaults.create_security_group, true)
+ create_task_exec_iam_role = try(each.value.create_task_exec_iam_role, var.defaults.create_task_exec_iam_role, false)
+ create_task_exec_policy = try(each.value.create_task_exec_policy, var.defaults.create_task_exec_policy, true)
+ default_capacity_provider_strategy = try(each.value.default_capacity_provider_strategy, var.defaults.default_capacity_provider_strategy, null)
+ disable_v7_default_name_description = try(each.value.disable_v7_default_name_description, var.defaults.disable_v7_default_name_description, false)
+ infrastructure_iam_role_description = try(each.value.infrastructure_iam_role_description, var.defaults.infrastructure_iam_role_description, null)
+ infrastructure_iam_role_name = try(each.value.infrastructure_iam_role_name, var.defaults.infrastructure_iam_role_name, null)
+ infrastructure_iam_role_override_policy_documents = try(each.value.infrastructure_iam_role_override_policy_documents, var.defaults.infrastructure_iam_role_override_policy_documents, [])
+ infrastructure_iam_role_path = try(each.value.infrastructure_iam_role_path, var.defaults.infrastructure_iam_role_path, null)
+ infrastructure_iam_role_permissions_boundary = try(each.value.infrastructure_iam_role_permissions_boundary, var.defaults.infrastructure_iam_role_permissions_boundary, null)
+ infrastructure_iam_role_source_policy_documents = try(each.value.infrastructure_iam_role_source_policy_documents, var.defaults.infrastructure_iam_role_source_policy_documents, [])
+ infrastructure_iam_role_statements = try(each.value.infrastructure_iam_role_statements, var.defaults.infrastructure_iam_role_statements, null)
+ infrastructure_iam_role_tags = try(each.value.infrastructure_iam_role_tags, var.defaults.infrastructure_iam_role_tags, {})
+ infrastructure_iam_role_use_name_prefix = try(each.value.infrastructure_iam_role_use_name_prefix, var.defaults.infrastructure_iam_role_use_name_prefix, true)
+ node_iam_role_additional_policies = try(each.value.node_iam_role_additional_policies, var.defaults.node_iam_role_additional_policies, {})
+ node_iam_role_description = try(each.value.node_iam_role_description, var.defaults.node_iam_role_description, "ECS Managed Instances node IAM role")
+ node_iam_role_name = try(each.value.node_iam_role_name, var.defaults.node_iam_role_name, null)
+ node_iam_role_override_policy_documents = try(each.value.node_iam_role_override_policy_documents, var.defaults.node_iam_role_override_policy_documents, [])
+ node_iam_role_path = try(each.value.node_iam_role_path, var.defaults.node_iam_role_path, null)
+ node_iam_role_permissions_boundary = try(each.value.node_iam_role_permissions_boundary, var.defaults.node_iam_role_permissions_boundary, null)
+ node_iam_role_source_policy_documents = try(each.value.node_iam_role_source_policy_documents, var.defaults.node_iam_role_source_policy_documents, [])
+ node_iam_role_statements = try(each.value.node_iam_role_statements, var.defaults.node_iam_role_statements, null)
+ node_iam_role_tags = try(each.value.node_iam_role_tags, var.defaults.node_iam_role_tags, {})
+ node_iam_role_use_name_prefix = try(each.value.node_iam_role_use_name_prefix, var.defaults.node_iam_role_use_name_prefix, true)
+ region = try(each.value.region, var.defaults.region, null)
+ security_group_description = try(each.value.security_group_description, var.defaults.security_group_description, null)
+ security_group_egress_rules = try(each.value.security_group_egress_rules, var.defaults.security_group_egress_rules, {
+ all_ipv4 = {
+ cidr_ipv4 = "0.0.0.0/0"
+ description = "Allow all IPv4 traffic"
+ ip_protocol = "-1"
+ }
+ all_ipv6 = {
+ cidr_ipv6 = "::/0"
+ description = "Allow all IPv6 traffic"
+ ip_protocol = "-1"
+ }
+ })
+ security_group_ingress_rules = try(each.value.security_group_ingress_rules, var.defaults.security_group_ingress_rules, {})
+ security_group_name = try(each.value.security_group_name, var.defaults.security_group_name, null)
+ security_group_tags = try(each.value.security_group_tags, var.defaults.security_group_tags, {})
+ security_group_use_name_prefix = try(each.value.security_group_use_name_prefix, var.defaults.security_group_use_name_prefix, true)
services = try(each.value.services, var.defaults.services, null)
tags = try(each.value.tags, var.defaults.tags, {})
task_exec_iam_role_description = try(each.value.task_exec_iam_role_description, var.defaults.task_exec_iam_role_description, null)
@@ -43,4 +84,5 @@ module "wrapper" {
task_exec_iam_statements = try(each.value.task_exec_iam_statements, var.defaults.task_exec_iam_statements, null)
task_exec_secret_arns = try(each.value.task_exec_secret_arns, var.defaults.task_exec_secret_arns, [])
task_exec_ssm_param_arns = try(each.value.task_exec_ssm_param_arns, var.defaults.task_exec_ssm_param_arns, [])
+ vpc_id = try(each.value.vpc_id, var.defaults.vpc_id, null)
}
diff --git a/wrappers/service/main.tf b/wrappers/service/main.tf
index 44ebf6be..a8b2d841 100644
--- a/wrappers/service/main.tf
+++ b/wrappers/service/main.tf
@@ -49,6 +49,7 @@ module "wrapper" {
deployment_maximum_percent = try(each.value.deployment_maximum_percent, var.defaults.deployment_maximum_percent, 200)
deployment_minimum_healthy_percent = try(each.value.deployment_minimum_healthy_percent, var.defaults.deployment_minimum_healthy_percent, 66)
desired_count = try(each.value.desired_count, var.defaults.desired_count, 1)
+ disable_v7_default_name_description = try(each.value.disable_v7_default_name_description, var.defaults.disable_v7_default_name_description, false)
enable_autoscaling = try(each.value.enable_autoscaling, var.defaults.enable_autoscaling, true)
enable_ecs_managed_tags = try(each.value.enable_ecs_managed_tags, var.defaults.enable_ecs_managed_tags, true)
enable_execute_command = try(each.value.enable_execute_command, var.defaults.enable_execute_command, false)
@@ -79,7 +80,7 @@ module "wrapper" {
launch_type = try(each.value.launch_type, var.defaults.launch_type, "FARGATE")
load_balancer = try(each.value.load_balancer, var.defaults.load_balancer, null)
memory = try(each.value.memory, var.defaults.memory, 2048)
- name = try(each.value.name, var.defaults.name, null)
+ name = try(each.value.name, var.defaults.name, "")
network_mode = try(each.value.network_mode, var.defaults.network_mode, "awsvpc")
ordered_placement_strategy = try(each.value.ordered_placement_strategy, var.defaults.ordered_placement_strategy, null)
pid_mode = try(each.value.pid_mode, var.defaults.pid_mode, null)
diff --git a/wrappers/service/versions.tf b/wrappers/service/versions.tf
index 70f23a44..7e5b918f 100644
--- a/wrappers/service/versions.tf
+++ b/wrappers/service/versions.tf
@@ -4,7 +4,7 @@ terraform {
required_providers {
aws = {
source = "hashicorp/aws"
- version = ">= 6.21"
+ version = ">= 6.23"
}
}
}
diff --git a/wrappers/versions.tf b/wrappers/versions.tf
index 70f23a44..7e5b918f 100644
--- a/wrappers/versions.tf
+++ b/wrappers/versions.tf
@@ -4,7 +4,7 @@ terraform {
required_providers {
aws = {
source = "hashicorp/aws"
- version = ">= 6.21"
+ version = ">= 6.23"
}
}
}