diff --git a/README.md b/README.md index 9aba8c9..8b75093 100644 --- a/README.md +++ b/README.md @@ -156,11 +156,13 @@ module "ecs" { | Name | Version | |------|---------| | [terraform](#requirement\_terraform) | >= 1.5.7 | -| [aws](#requirement\_aws) | >= 6.14 | +| [aws](#requirement\_aws) | >= 6.15 | ## Providers -No providers. +| Name | Version | +|------|---------| +| [time](#provider\_time) | n/a | ## Modules @@ -171,13 +173,16 @@ No providers. ## Resources -No resources. +| Name | Type | +|------|------| +| [time_sleep.this](https://registry.terraform.io/providers/hashicorp/time/latest/docs/resources/sleep) | resource | ## Inputs | 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 | +| [autoscaling\_capacity\_providers](#input\_autoscaling\_capacity\_providers) | [DEPRECATED - use `capacity_providers` instead] 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 | @@ -190,10 +195,38 @@ 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 | +| [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 | +| [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)
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)
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)
})))
# 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 | @@ -206,18 +239,30 @@ 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 | +| [wait\_duration](#input\_wait\_duration) | Duration to wait between ECS cluster and ECS service (to give enough time for capacity providers to reach ACTIVE state) | `string` | `"60s"` | no | ## Outputs | Name | Description | |------|-------------| -| [autoscaling\_capacity\_providers](#output\_autoscaling\_capacity\_providers) | Map of autoscaling capacity providers created and their attributes | +| [autoscaling\_capacity\_providers](#output\_autoscaling\_capacity\_providers) | [DEPRECATED - use `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/examples/complete/README.md b/examples/complete/README.md index 2477a11..7b9ac76 100644 --- a/examples/complete/README.md +++ b/examples/complete/README.md @@ -14,9 +14,9 @@ Configuration in this directory creates: To run this example you need to execute: ```bash -$ terraform init -$ terraform plan -$ terraform apply +terraform init +terraform plan +terraform apply ``` Note that this example may create resources which will incur monetary charges on your AWS bill. Run `terraform destroy` when you no longer need these resources. @@ -27,13 +27,13 @@ Note that this example may create resources which will incur monetary charges on | Name | Version | |------|---------| | [terraform](#requirement\_terraform) | >= 1.5.7 | -| [aws](#requirement\_aws) | >= 6.14 | +| [aws](#requirement\_aws) | >= 6.15 | ## Providers | Name | Version | |------|---------| -| [aws](#provider\_aws) | >= 6.14 | +| [aws](#provider\_aws) | >= 6.15 | ## Modules @@ -66,12 +66,26 @@ No inputs. | Name | Description | |------|-------------| | [alb\_dns\_name](#output\_alb\_dns\_name) | The DNS name of the load balancer | +| [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\_autoscaling\_capacity\_providers](#output\_cluster\_autoscaling\_capacity\_providers) | Map of capacity providers created and their attributes | | [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 | +| [task\_exec\_iam\_role\_unique\_id](#output\_task\_exec\_iam\_role\_unique\_id) | Stable and unique string identifying the task execution IAM role | ## License diff --git a/examples/complete/outputs.tf b/examples/complete/outputs.tf index dd0cdd2..e261408 100644 --- a/examples/complete/outputs.tf +++ b/examples/complete/outputs.tf @@ -17,14 +17,84 @@ output "cluster_name" { value = module.ecs.cluster_name } +output "cloudwatch_log_group_name" { + description = "Name of CloudWatch log group created" + value = module.ecs.cloudwatch_log_group_name +} + +output "cloudwatch_log_group_arn" { + description = "ARN of CloudWatch log group created" + value = module.ecs.cloudwatch_log_group_arn +} + output "cluster_capacity_providers" { description = "Map of cluster capacity providers attributes" value = module.ecs.cluster_capacity_providers } -output "cluster_autoscaling_capacity_providers" { - description = "Map of capacity providers created and their attributes" - value = module.ecs.autoscaling_capacity_providers +output "capacity_providers" { + description = "Map of autoscaling capacity providers created and their attributes" + value = module.ecs.capacity_providers +} + +output "task_exec_iam_role_name" { + description = "Task execution IAM role name" + value = module.ecs.task_exec_iam_role_name +} + +output "task_exec_iam_role_arn" { + description = "Task execution IAM role ARN" + value = module.ecs.task_exec_iam_role_arn +} + +output "task_exec_iam_role_unique_id" { + description = "Stable and unique string identifying the task execution IAM role" + value = module.ecs.task_exec_iam_role_unique_id +} + +output "infrastructure_iam_role_arn" { + description = "The Amazon Resource Name (ARN) specifying the IAM role" + value = module.ecs.infrastructure_iam_role_arn +} + +output "infrastructure_iam_role_name" { + description = "IAM role name" + value = module.ecs.infrastructure_iam_role_name +} + +output "infrastructure_iam_role_unique_id" { + description = "Stable and unique string identifying the IAM role" + value = module.ecs.infrastructure_iam_role_unique_id +} + +output "node_iam_role_arn" { + description = "The Amazon Resource Name (ARN) specifying the IAM role" + value = module.ecs.node_iam_role_arn +} + +output "node_iam_role_name" { + description = "IAM role name" + value = module.ecs.node_iam_role_name +} + +output "node_iam_role_unique_id" { + description = "Stable and unique string identifying the IAM role" + value = module.ecs.node_iam_role_unique_id +} + +output "node_iam_instance_profile_arn" { + description = "ARN assigned by AWS to the instance profile" + value = module.ecs.node_iam_instance_profile_arn +} + +output "node_iam_instance_profile_id" { + description = "Instance profile's ID" + value = module.ecs.node_iam_instance_profile_id +} + +output "node_iam_instance_profile_unique" { + description = "Stable and unique string identifying the IAM instance profile" + value = module.ecs.node_iam_instance_profile_unique } ################################################################################ diff --git a/examples/complete/versions.tf b/examples/complete/versions.tf index 0e90b8c..e1ac31c 100644 --- a/examples/complete/versions.tf +++ b/examples/complete/versions.tf @@ -4,7 +4,7 @@ terraform { required_providers { aws = { source = "hashicorp/aws" - version = ">= 6.14" + version = ">= 6.15" } } } diff --git a/examples/container-definition/README.md b/examples/container-definition/README.md index 3407b50..7adba22 100644 --- a/examples/container-definition/README.md +++ b/examples/container-definition/README.md @@ -9,9 +9,9 @@ Configuration in this directory creates: To run this example you need to execute: ```bash -$ terraform init -$ terraform plan -$ terraform apply +terraform init +terraform plan +terraform apply ``` Note that this example may create resources which will incur monetary charges on your AWS bill. Run `terraform destroy` when you no longer need these resources. @@ -22,7 +22,7 @@ Note that this example may create resources which will incur monetary charges on | Name | Version | |------|---------| | [terraform](#requirement\_terraform) | >= 1.5.7 | -| [aws](#requirement\_aws) | >= 6.14 | +| [aws](#requirement\_aws) | >= 6.15 | | [null](#requirement\_null) | >= 3.2 | ## Providers diff --git a/examples/container-definition/versions.tf b/examples/container-definition/versions.tf index 5c1a2c3..21f498a 100644 --- a/examples/container-definition/versions.tf +++ b/examples/container-definition/versions.tf @@ -4,7 +4,7 @@ terraform { required_providers { aws = { source = "hashicorp/aws" - version = ">= 6.14" + version = ">= 6.15" } null = { source = "hashicorp/null" diff --git a/examples/ec2-autoscaling/README.md b/examples/ec2-autoscaling/README.md index 62a19dc..7803f1d 100644 --- a/examples/ec2-autoscaling/README.md +++ b/examples/ec2-autoscaling/README.md @@ -14,9 +14,9 @@ Configuration in this directory creates: To run this example you need to execute: ```bash -$ terraform init -$ terraform plan -$ terraform apply +terraform init +terraform plan +terraform apply ``` Note that this example may create resources which will incur monetary charges on your AWS bill. Run `terraform destroy` when you no longer need these resources. @@ -27,13 +27,13 @@ Note that this example may create resources which will incur monetary charges on | Name | Version | |------|---------| | [terraform](#requirement\_terraform) | >= 1.5.7 | -| [aws](#requirement\_aws) | >= 6.14 | +| [aws](#requirement\_aws) | >= 6.15 | ## Providers | Name | Version | |------|---------| -| [aws](#provider\_aws) | >= 6.14 | +| [aws](#provider\_aws) | >= 6.15 | ## Modules @@ -62,11 +62,22 @@ No inputs. | Name | Description | |------|-------------| | [alb\_dns\_name](#output\_alb\_dns\_name) | The DNS name of the load balancer | +| [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\_autoscaling\_capacity\_providers](#output\_cluster\_autoscaling\_capacity\_providers) | Map of capacity providers created and their attributes | | [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 | | [service\_autoscaling\_policies](#output\_service\_autoscaling\_policies) | Map of autoscaling policies and their attributes | | [service\_autoscaling\_scheduled\_actions](#output\_service\_autoscaling\_scheduled\_actions) | Map of autoscaling scheduled actions and their attributes | | [service\_container\_definitions](#output\_service\_container\_definitions) | Container definitions | @@ -89,6 +100,9 @@ No inputs. | [service\_tasks\_iam\_role\_arn](#output\_service\_tasks\_iam\_role\_arn) | Tasks IAM role ARN | | [service\_tasks\_iam\_role\_name](#output\_service\_tasks\_iam\_role\_name) | Tasks IAM role name | | [service\_tasks\_iam\_role\_unique\_id](#output\_service\_tasks\_iam\_role\_unique\_id) | Stable and unique string identifying the tasks IAM role | +| [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 | ## License diff --git a/examples/ec2-autoscaling/outputs.tf b/examples/ec2-autoscaling/outputs.tf index 3ad6294..9179fd9 100644 --- a/examples/ec2-autoscaling/outputs.tf +++ b/examples/ec2-autoscaling/outputs.tf @@ -17,14 +17,84 @@ output "cluster_name" { value = module.ecs_cluster.name } +output "cloudwatch_log_group_name" { + description = "Name of CloudWatch log group created" + value = module.ecs_cluster.cloudwatch_log_group_name +} + +output "cloudwatch_log_group_arn" { + description = "ARN of CloudWatch log group created" + value = module.ecs_cluster.cloudwatch_log_group_arn +} + output "cluster_capacity_providers" { description = "Map of cluster capacity providers attributes" value = module.ecs_cluster.cluster_capacity_providers } -output "cluster_autoscaling_capacity_providers" { - description = "Map of capacity providers created and their attributes" - value = module.ecs_cluster.autoscaling_capacity_providers +output "capacity_providers" { + description = "Map of autoscaling capacity providers created and their attributes" + value = module.ecs_cluster.capacity_providers +} + +output "task_exec_iam_role_name" { + description = "Task execution IAM role name" + value = module.ecs_cluster.task_exec_iam_role_name +} + +output "task_exec_iam_role_arn" { + description = "Task execution IAM role ARN" + value = module.ecs_cluster.task_exec_iam_role_arn +} + +output "task_exec_iam_role_unique_id" { + description = "Stable and unique string identifying the task execution IAM role" + value = module.ecs_cluster.task_exec_iam_role_unique_id +} + +output "infrastructure_iam_role_arn" { + description = "The Amazon Resource Name (ARN) specifying the IAM role" + value = module.ecs_cluster.infrastructure_iam_role_arn +} + +output "infrastructure_iam_role_name" { + description = "IAM role name" + value = module.ecs_cluster.infrastructure_iam_role_name +} + +output "infrastructure_iam_role_unique_id" { + description = "Stable and unique string identifying the IAM role" + value = module.ecs_cluster.infrastructure_iam_role_unique_id +} + +output "node_iam_role_arn" { + description = "The Amazon Resource Name (ARN) specifying the IAM role" + value = module.ecs_cluster.node_iam_role_arn +} + +output "node_iam_role_name" { + description = "IAM role name" + value = module.ecs_cluster.node_iam_role_name +} + +output "node_iam_role_unique_id" { + description = "Stable and unique string identifying the IAM role" + value = module.ecs_cluster.node_iam_role_unique_id +} + +output "node_iam_instance_profile_arn" { + description = "ARN assigned by AWS to the instance profile" + value = module.ecs_cluster.node_iam_instance_profile_arn +} + +output "node_iam_instance_profile_id" { + description = "Instance profile's ID" + value = module.ecs_cluster.node_iam_instance_profile_id +} + +output "node_iam_instance_profile_unique" { + description = "Stable and unique string identifying the IAM instance profile" + value = module.ecs_cluster.node_iam_instance_profile_unique } ################################################################################ diff --git a/examples/ec2-autoscaling/versions.tf b/examples/ec2-autoscaling/versions.tf index 0e90b8c..e1ac31c 100644 --- a/examples/ec2-autoscaling/versions.tf +++ b/examples/ec2-autoscaling/versions.tf @@ -4,7 +4,7 @@ terraform { required_providers { aws = { source = "hashicorp/aws" - version = ">= 6.14" + version = ">= 6.15" } } } diff --git a/examples/fargate/README.md b/examples/fargate/README.md index 39a3b0e..faa809a 100644 --- a/examples/fargate/README.md +++ b/examples/fargate/README.md @@ -14,9 +14,9 @@ Configuration in this directory creates: To run this example you need to execute: ```bash -$ terraform init -$ terraform plan -$ terraform apply +terraform init +terraform plan +terraform apply ``` Note that this example may create resources which will incur monetary charges on your AWS bill. Run `terraform destroy` when you no longer need these resources. @@ -27,13 +27,13 @@ Note that this example may create resources which will incur monetary charges on | Name | Version | |------|---------| | [terraform](#requirement\_terraform) | >= 1.5.7 | -| [aws](#requirement\_aws) | >= 6.14 | +| [aws](#requirement\_aws) | >= 6.15 | ## Providers | Name | Version | |------|---------| -| [aws](#provider\_aws) | >= 6.14 | +| [aws](#provider\_aws) | >= 6.15 | ## Modules @@ -64,11 +64,22 @@ No inputs. | Name | Description | |------|-------------| +| [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\_autoscaling\_capacity\_providers](#output\_cluster\_autoscaling\_capacity\_providers) | Map of capacity providers created and their attributes | | [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 | | [service\_autoscaling\_policies](#output\_service\_autoscaling\_policies) | Map of autoscaling policies and their attributes | | [service\_autoscaling\_scheduled\_actions](#output\_service\_autoscaling\_scheduled\_actions) | Map of autoscaling scheduled actions and their attributes | | [service\_container\_definitions](#output\_service\_container\_definitions) | Container definitions | @@ -93,6 +104,9 @@ No inputs. | [service\_tasks\_iam\_role\_name](#output\_service\_tasks\_iam\_role\_name) | Tasks IAM role name | | [service\_tasks\_iam\_role\_unique\_id](#output\_service\_tasks\_iam\_role\_unique\_id) | Stable and unique string identifying the tasks IAM role | | [task\_definition\_run\_task\_command](#output\_task\_definition\_run\_task\_command) | awscli command to run the standalone task | +| [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 | ## License diff --git a/examples/fargate/outputs.tf b/examples/fargate/outputs.tf index 6f33f2f..5256899 100644 --- a/examples/fargate/outputs.tf +++ b/examples/fargate/outputs.tf @@ -17,14 +17,84 @@ output "cluster_name" { value = module.ecs_cluster.name } +output "cloudwatch_log_group_name" { + description = "Name of CloudWatch log group created" + value = module.ecs_cluster.cloudwatch_log_group_name +} + +output "cloudwatch_log_group_arn" { + description = "ARN of CloudWatch log group created" + value = module.ecs_cluster.cloudwatch_log_group_arn +} + output "cluster_capacity_providers" { description = "Map of cluster capacity providers attributes" value = module.ecs_cluster.cluster_capacity_providers } -output "cluster_autoscaling_capacity_providers" { - description = "Map of capacity providers created and their attributes" - value = module.ecs_cluster.autoscaling_capacity_providers +output "capacity_providers" { + description = "Map of autoscaling capacity providers created and their attributes" + value = module.ecs_cluster.capacity_providers +} + +output "task_exec_iam_role_name" { + description = "Task execution IAM role name" + value = module.ecs_cluster.task_exec_iam_role_name +} + +output "task_exec_iam_role_arn" { + description = "Task execution IAM role ARN" + value = module.ecs_cluster.task_exec_iam_role_arn +} + +output "task_exec_iam_role_unique_id" { + description = "Stable and unique string identifying the task execution IAM role" + value = module.ecs_cluster.task_exec_iam_role_unique_id +} + +output "infrastructure_iam_role_arn" { + description = "The Amazon Resource Name (ARN) specifying the IAM role" + value = module.ecs_cluster.infrastructure_iam_role_arn +} + +output "infrastructure_iam_role_name" { + description = "IAM role name" + value = module.ecs_cluster.infrastructure_iam_role_name +} + +output "infrastructure_iam_role_unique_id" { + description = "Stable and unique string identifying the IAM role" + value = module.ecs_cluster.infrastructure_iam_role_unique_id +} + +output "node_iam_role_arn" { + description = "The Amazon Resource Name (ARN) specifying the IAM role" + value = module.ecs_cluster.node_iam_role_arn +} + +output "node_iam_role_name" { + description = "IAM role name" + value = module.ecs_cluster.node_iam_role_name +} + +output "node_iam_role_unique_id" { + description = "Stable and unique string identifying the IAM role" + value = module.ecs_cluster.node_iam_role_unique_id +} + +output "node_iam_instance_profile_arn" { + description = "ARN assigned by AWS to the instance profile" + value = module.ecs_cluster.node_iam_instance_profile_arn +} + +output "node_iam_instance_profile_id" { + description = "Instance profile's ID" + value = module.ecs_cluster.node_iam_instance_profile_id +} + +output "node_iam_instance_profile_unique" { + description = "Stable and unique string identifying the IAM instance profile" + value = module.ecs_cluster.node_iam_instance_profile_unique } ################################################################################ diff --git a/examples/fargate/versions.tf b/examples/fargate/versions.tf index 0e90b8c..e1ac31c 100644 --- a/examples/fargate/versions.tf +++ b/examples/fargate/versions.tf @@ -4,7 +4,7 @@ terraform { required_providers { aws = { source = "hashicorp/aws" - version = ">= 6.14" + version = ">= 6.15" } } } diff --git a/examples/managed-instances/README.md b/examples/managed-instances/README.md new file mode 100644 index 0000000..883d191 --- /dev/null +++ b/examples/managed-instances/README.md @@ -0,0 +1,107 @@ +# ECS Clusters w/ ECS Managed Instances + +Configuration in this directory creates: + +- ECS cluster using ECS Managed Instances capacity provider +- Example ECS service that utilizes + - AWS Firelens using FluentBit sidecar container definition + - Service connect configuration + - Load balancer target group attachment + - Security group for access to the example service + +## Usage + +To run this example you need to execute: + +```bash +terraform init +terraform plan +terraform apply +``` + +Note that this example may create resources which will incur monetary charges on your AWS bill. Run `terraform destroy` when you no longer need these resources. + + +## Requirements + +| Name | Version | +|------|---------| +| [terraform](#requirement\_terraform) | >= 1.5.7 | +| [aws](#requirement\_aws) | >= 6.15 | + +## Providers + +| Name | Version | +|------|---------| +| [aws](#provider\_aws) | >= 6.15 | + +## Modules + +| Name | Source | Version | +|------|--------|---------| +| [alb](#module\_alb) | terraform-aws-modules/alb/aws | ~> 10.0 | +| [ecs\_cluster](#module\_ecs\_cluster) | ../../modules/cluster | n/a | +| [ecs\_service](#module\_ecs\_service) | ../../modules/service | n/a | +| [vpc](#module\_vpc) | terraform-aws-modules/vpc/aws | ~> 6.0 | + +## Resources + +| Name | Type | +|------|------| +| [aws_availability_zones.available](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/availability_zones) | data source | + +## Inputs + +No inputs. + +## Outputs + +| Name | Description | +|------|-------------| +| [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 | +| [service\_autoscaling\_policies](#output\_service\_autoscaling\_policies) | Map of autoscaling policies and their attributes | +| [service\_autoscaling\_scheduled\_actions](#output\_service\_autoscaling\_scheduled\_actions) | Map of autoscaling scheduled actions and their attributes | +| [service\_container\_definitions](#output\_service\_container\_definitions) | Container definitions | +| [service\_iam\_role\_arn](#output\_service\_iam\_role\_arn) | Service IAM role ARN | +| [service\_iam\_role\_name](#output\_service\_iam\_role\_name) | Service IAM role name | +| [service\_iam\_role\_unique\_id](#output\_service\_iam\_role\_unique\_id) | Stable and unique string identifying the service IAM role | +| [service\_id](#output\_service\_id) | ARN that identifies the service | +| [service\_name](#output\_service\_name) | Name of the service | +| [service\_security\_group\_arn](#output\_service\_security\_group\_arn) | Amazon Resource Name (ARN) of the security group | +| [service\_security\_group\_id](#output\_service\_security\_group\_id) | ID of the security group | +| [service\_task\_definition\_arn](#output\_service\_task\_definition\_arn) | Full ARN of the Task Definition (including both `family` and `revision`) | +| [service\_task\_definition\_family](#output\_service\_task\_definition\_family) | The unique name of the task definition | +| [service\_task\_definition\_revision](#output\_service\_task\_definition\_revision) | Revision of the task in a particular family | +| [service\_task\_exec\_iam\_role\_arn](#output\_service\_task\_exec\_iam\_role\_arn) | Task execution IAM role ARN | +| [service\_task\_exec\_iam\_role\_name](#output\_service\_task\_exec\_iam\_role\_name) | Task execution IAM role name | +| [service\_task\_exec\_iam\_role\_unique\_id](#output\_service\_task\_exec\_iam\_role\_unique\_id) | Stable and unique string identifying the task execution IAM role | +| [service\_task\_set\_arn](#output\_service\_task\_set\_arn) | The Amazon Resource Name (ARN) that identifies the task set | +| [service\_task\_set\_id](#output\_service\_task\_set\_id) | The ID of the task set | +| [service\_task\_set\_stability\_status](#output\_service\_task\_set\_stability\_status) | The stability status. This indicates whether the task set has reached a steady state | +| [service\_task\_set\_status](#output\_service\_task\_set\_status) | The status of the task set | +| [service\_tasks\_iam\_role\_arn](#output\_service\_tasks\_iam\_role\_arn) | Tasks IAM role ARN | +| [service\_tasks\_iam\_role\_name](#output\_service\_tasks\_iam\_role\_name) | Tasks IAM role name | +| [service\_tasks\_iam\_role\_unique\_id](#output\_service\_tasks\_iam\_role\_unique\_id) | Stable and unique string identifying the tasks IAM role | +| [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 | + + +## License + +Apache-2.0 Licensed. See [LICENSE](https://github.com/terraform-aws-modules/terraform-aws-ecs/blob/master/LICENSE). diff --git a/examples/managed-instances/main.tf b/examples/managed-instances/main.tf new file mode 100644 index 0000000..d7977a1 --- /dev/null +++ b/examples/managed-instances/main.tf @@ -0,0 +1,246 @@ +provider "aws" { + region = local.region +} + +data "aws_availability_zones" "available" {} + +locals { + region = "eu-west-1" + name = "ex-${basename(path.cwd)}" + + vpc_cidr = "10.0.0.0/16" + azs = slice(data.aws_availability_zones.available.names, 0, 3) + + container_name = "ecs-sample" + container_port = 80 + + tags = { + Name = local.name + Example = local.name + Repository = "https://github.com/terraform-aws-modules/terraform-aws-ecs" + } +} + +################################################################################ +# Cluster +################################################################################ + +module "ecs_cluster" { + source = "../../modules/cluster" + + name = local.name + + capacity_providers = { + mi-example = { + managed_instances_provider = { + instance_launch_template = { + instance_requirements = { + instance_generations = ["current"] + cpu_manufacturers = ["intel", "amd"] + + memory_mib = { + max = 8192 + min = 1024 + } + + vcpu_count = { + max = 4 + min = 1 + } + } + + network_configuration = { + subnets = module.vpc.private_subnets + } + + storage_configuration = { + storage_size_gib = 30 + } + } + } + } + } + + default_capacity_provider_strategy = { + # Managed instances provider created above + mi-example = { + weight = 1 + } + } + + # Managed instances security group + vpc_id = module.vpc.vpc_id + security_group_ingress_rules = { + alb_http = { + from_port = local.container_port + description = "Service port" + referenced_security_group_id = module.alb.security_group_id + } + } + security_group_egress_rules = { + all = { + cidr_ipv4 = "0.0.0.0/0" + ip_protocol = "-1" + } + } + + tags = local.tags +} + +################################################################################ +# Service +################################################################################ + +module "ecs_service" { + source = "../../modules/service" + + # Service + name = local.name + cluster_arn = module.ecs_cluster.arn + + # Task Definition + requires_compatibilities = ["MANAGED_INSTANCES"] + launch_type = "EC2" + + # Container definition(s) + container_definitions = { + (local.container_name) = { + image = "public.ecr.aws/docker/library/httpd:latest" + + essential = true + entrypoint = ["sh", "-c"] + command = ["/bin/sh -c \"echo 'Amazon ECS Sample App

Amazon ECS Sample App

Congratulations!

Your application is now running on a container in Amazon ECS using Amazon ECS Managed Instances.

' > /usr/local/apache2/htdocs/index.html && httpd-foreground\""] + + cpu = 256 + memory = 512 + + readonlyRootFilesystem = false + + portMappings = [ + { + name = local.container_name + containerPort = local.container_port + hostPort = local.container_port + protocol = "tcp" + } + ] + } + } + + capacity_provider_strategy = { + # On-demand instances + mi-example = { + capacity_provider = module.ecs_cluster.capacity_providers["mi-example"].name + } + } + + load_balancer = { + service = { + target_group_arn = module.alb.target_groups["ex_ecs"].arn + container_name = local.container_name + container_port = local.container_port + } + } + + subnet_ids = module.vpc.private_subnets + security_group_ingress_rules = { + alb_http = { + from_port = local.container_port + description = "Service port" + referenced_security_group_id = module.alb.security_group_id + } + } + + tags = local.tags +} + +################################################################################ +# Supporting Resources +################################################################################ + +module "alb" { + source = "terraform-aws-modules/alb/aws" + version = "~> 10.0" + + name = local.name + + load_balancer_type = "application" + + vpc_id = module.vpc.vpc_id + subnets = module.vpc.public_subnets + + # For example only + enable_deletion_protection = false + + # Security Group + security_group_ingress_rules = { + all_http = { + from_port = local.container_port + to_port = local.container_port + ip_protocol = "tcp" + cidr_ipv4 = "0.0.0.0/0" + } + } + security_group_egress_rules = { + all = { + ip_protocol = "-1" + cidr_ipv4 = module.vpc.vpc_cidr_block + } + } + + listeners = { + ex_http = { + port = local.container_port + protocol = "HTTP" + + forward = { + target_group_key = "ex_ecs" + } + } + } + + target_groups = { + ex_ecs = { + backend_protocol = "HTTP" + backend_port = local.container_port + target_type = "ip" + deregistration_delay = 5 + load_balancing_cross_zone_enabled = true + + health_check = { + enabled = true + healthy_threshold = 5 + interval = 30 + matcher = "200" + path = "/" + port = "traffic-port" + protocol = "HTTP" + timeout = 5 + unhealthy_threshold = 2 + } + + # Theres nothing to attach here in this definition. Instead, + # ECS will attach the IPs of the tasks to this target group + create_attachment = false + } + } + + tags = local.tags +} + +module "vpc" { + source = "terraform-aws-modules/vpc/aws" + version = "~> 6.0" + + name = local.name + cidr = local.vpc_cidr + + azs = local.azs + private_subnets = [for k, v in local.azs : cidrsubnet(local.vpc_cidr, 4, k)] + public_subnets = [for k, v in local.azs : cidrsubnet(local.vpc_cidr, 8, k + 48)] + + enable_nat_gateway = true + single_nat_gateway = true + + tags = local.tags +} diff --git a/examples/managed-instances/outputs.tf b/examples/managed-instances/outputs.tf new file mode 100644 index 0000000..4350622 --- /dev/null +++ b/examples/managed-instances/outputs.tf @@ -0,0 +1,217 @@ +################################################################################ +# Cluster +################################################################################ + +output "cluster_arn" { + description = "ARN that identifies the cluster" + value = module.ecs_cluster.arn +} + +output "cluster_id" { + description = "ID that identifies the cluster" + value = module.ecs_cluster.id +} + +output "cluster_name" { + description = "Name that identifies the cluster" + value = module.ecs_cluster.name +} + +output "cloudwatch_log_group_name" { + description = "Name of CloudWatch log group created" + value = module.ecs_cluster.cloudwatch_log_group_name +} + +output "cloudwatch_log_group_arn" { + description = "ARN of CloudWatch log group created" + value = module.ecs_cluster.cloudwatch_log_group_arn +} + +output "cluster_capacity_providers" { + description = "Map of cluster capacity providers attributes" + value = module.ecs_cluster.cluster_capacity_providers +} + +output "capacity_providers" { + description = "Map of autoscaling capacity providers created and their attributes" + value = module.ecs_cluster.capacity_providers +} + +output "task_exec_iam_role_name" { + description = "Task execution IAM role name" + value = module.ecs_cluster.task_exec_iam_role_name +} + +output "task_exec_iam_role_arn" { + description = "Task execution IAM role ARN" + value = module.ecs_cluster.task_exec_iam_role_arn +} + +output "task_exec_iam_role_unique_id" { + description = "Stable and unique string identifying the task execution IAM role" + value = module.ecs_cluster.task_exec_iam_role_unique_id +} + +output "infrastructure_iam_role_arn" { + description = "The Amazon Resource Name (ARN) specifying the IAM role" + value = module.ecs_cluster.infrastructure_iam_role_arn +} + +output "infrastructure_iam_role_name" { + description = "IAM role name" + value = module.ecs_cluster.infrastructure_iam_role_name +} + +output "infrastructure_iam_role_unique_id" { + description = "Stable and unique string identifying the IAM role" + value = module.ecs_cluster.infrastructure_iam_role_unique_id +} + +output "node_iam_role_arn" { + description = "The Amazon Resource Name (ARN) specifying the IAM role" + value = module.ecs_cluster.node_iam_role_arn +} + +output "node_iam_role_name" { + description = "IAM role name" + value = module.ecs_cluster.node_iam_role_name +} + +output "node_iam_role_unique_id" { + description = "Stable and unique string identifying the IAM role" + value = module.ecs_cluster.node_iam_role_unique_id +} + +output "node_iam_instance_profile_arn" { + description = "ARN assigned by AWS to the instance profile" + value = module.ecs_cluster.node_iam_instance_profile_arn +} + +output "node_iam_instance_profile_id" { + description = "Instance profile's ID" + value = module.ecs_cluster.node_iam_instance_profile_id +} + +output "node_iam_instance_profile_unique" { + description = "Stable and unique string identifying the IAM instance profile" + value = module.ecs_cluster.node_iam_instance_profile_unique +} + +################################################################################ +# Service +################################################################################ + +output "service_id" { + description = "ARN that identifies the service" + value = module.ecs_service.id +} + +output "service_name" { + description = "Name of the service" + value = module.ecs_service.name +} + +output "service_iam_role_name" { + description = "Service IAM role name" + value = module.ecs_service.iam_role_name +} + +output "service_iam_role_arn" { + description = "Service IAM role ARN" + value = module.ecs_service.iam_role_arn +} + +output "service_iam_role_unique_id" { + description = "Stable and unique string identifying the service IAM role" + value = module.ecs_service.iam_role_unique_id +} + +output "service_container_definitions" { + description = "Container definitions" + value = module.ecs_service.container_definitions +} + +output "service_task_definition_arn" { + description = "Full ARN of the Task Definition (including both `family` and `revision`)" + value = module.ecs_service.task_definition_arn +} + +output "service_task_definition_revision" { + description = "Revision of the task in a particular family" + value = module.ecs_service.task_definition_revision +} + +output "service_task_definition_family" { + description = "The unique name of the task definition" + value = module.ecs_service.task_definition_family +} + +output "service_task_exec_iam_role_name" { + description = "Task execution IAM role name" + value = module.ecs_service.task_exec_iam_role_name +} + +output "service_task_exec_iam_role_arn" { + description = "Task execution IAM role ARN" + value = module.ecs_service.task_exec_iam_role_arn +} + +output "service_task_exec_iam_role_unique_id" { + description = "Stable and unique string identifying the task execution IAM role" + value = module.ecs_service.task_exec_iam_role_unique_id +} + +output "service_tasks_iam_role_name" { + description = "Tasks IAM role name" + value = module.ecs_service.tasks_iam_role_name +} + +output "service_tasks_iam_role_arn" { + description = "Tasks IAM role ARN" + value = module.ecs_service.tasks_iam_role_arn +} + +output "service_tasks_iam_role_unique_id" { + description = "Stable and unique string identifying the tasks IAM role" + value = module.ecs_service.tasks_iam_role_unique_id +} + +output "service_task_set_id" { + description = "The ID of the task set" + value = module.ecs_service.task_set_id +} + +output "service_task_set_arn" { + description = "The Amazon Resource Name (ARN) that identifies the task set" + value = module.ecs_service.task_set_arn +} + +output "service_task_set_stability_status" { + description = "The stability status. This indicates whether the task set has reached a steady state" + value = module.ecs_service.task_set_stability_status +} + +output "service_task_set_status" { + description = "The status of the task set" + value = module.ecs_service.task_set_status +} + +output "service_autoscaling_policies" { + description = "Map of autoscaling policies and their attributes" + value = module.ecs_service.autoscaling_policies +} + +output "service_autoscaling_scheduled_actions" { + description = "Map of autoscaling scheduled actions and their attributes" + value = module.ecs_service.autoscaling_scheduled_actions +} + +output "service_security_group_arn" { + description = "Amazon Resource Name (ARN) of the security group" + value = module.ecs_service.security_group_arn +} + +output "service_security_group_id" { + description = "ID of the security group" + value = module.ecs_service.security_group_id +} diff --git a/examples/managed-instances/variables.tf b/examples/managed-instances/variables.tf new file mode 100644 index 0000000..e69de29 diff --git a/examples/managed-instances/versions.tf b/examples/managed-instances/versions.tf new file mode 100644 index 0000000..e1ac31c --- /dev/null +++ b/examples/managed-instances/versions.tf @@ -0,0 +1,10 @@ +terraform { + required_version = ">= 1.5.7" + + required_providers { + aws = { + source = "hashicorp/aws" + version = ">= 6.15" + } + } +} diff --git a/main.tf b/main.tf index e464d9b..575e4d7 100644 --- a/main.tf +++ b/main.tf @@ -1,3 +1,18 @@ +# Time needed to allow capacity provider to reach `ACTIVE` state before +# A service can be created using it. Otherwise you get the error: +# +# InvalidParameterException: The capacity provider specified in capacity provider +# strategy is not ACTIVE. Please try again when capacity provider is ACTIVE. +resource "time_sleep" "this" { + count = var.create ? 1 : 0 + + create_duration = var.wait_duration + + triggers = { + capacity_providers = [for cp in module.cluster.capacity_providers : cp.id] + } +} + ################################################################################ # Cluster ################################################################################ @@ -24,6 +39,7 @@ module "cluster" { # Cluster capacity providers autoscaling_capacity_providers = var.autoscaling_capacity_providers + capacity_providers = var.capacity_providers default_capacity_provider_strategy = var.default_capacity_provider_strategy # Task execution IAM role @@ -42,6 +58,47 @@ module "cluster" { task_exec_secret_arns = var.task_exec_secret_arns task_exec_iam_statements = var.task_exec_iam_statements + # -- ECS Managed Instances -- + + # Infrastructure IAM role + create_infrastructure_iam_role = var.create_infrastructure_iam_role + infrastructure_iam_role_name = var.infrastructure_iam_role_name + infrastructure_iam_role_use_name_prefix = var.infrastructure_iam_role_use_name_prefix + infrastructure_iam_role_path = var.infrastructure_iam_role_path + infrastructure_iam_role_description = var.infrastructure_iam_role_description + infrastructure_iam_role_permissions_boundary = var.infrastructure_iam_role_permissions_boundary + infrastructure_iam_role_tags = var.infrastructure_iam_role_tags + + # Infrastructure IAM role policy + infrastructure_iam_role_source_policy_documents = var.infrastructure_iam_role_source_policy_documents + infrastructure_iam_role_override_policy_documents = var.infrastructure_iam_role_override_policy_documents + infrastructure_iam_role_statements = var.infrastructure_iam_role_statements + + # Node IAM role & instance profile + create_node_iam_instance_profile = var.create_node_iam_instance_profile + node_iam_role_name = var.node_iam_role_name + node_iam_role_use_name_prefix = var.node_iam_role_use_name_prefix + node_iam_role_path = var.node_iam_role_path + node_iam_role_description = var.node_iam_role_description + node_iam_role_permissions_boundary = var.node_iam_role_permissions_boundary + node_iam_role_additional_policies = var.node_iam_role_additional_policies + node_iam_role_tags = var.node_iam_role_tags + + # Node IAM role policy + node_iam_role_source_policy_documents = var.node_iam_role_source_policy_documents + node_iam_role_override_policy_documents = var.node_iam_role_override_policy_documents + node_iam_role_statements = var.node_iam_role_statements + + # Security Group + create_security_group = var.create_security_group + vpc_id = var.vpc_id + security_group_name = var.security_group_name + security_group_use_name_prefix = var.security_group_use_name_prefix + security_group_description = var.security_group_description + security_group_ingress_rules = var.security_group_ingress_rules + security_group_egress_rules = var.security_group_egress_rules + security_group_tags = var.security_group_tags + tags = merge(var.tags, var.cluster_tags) } @@ -193,4 +250,8 @@ module "service" { infrastructure_iam_role_tags = each.value.infrastructure_iam_role_tags tags = merge(var.tags, each.value.tags) + + depends_on = [ + time_sleep.this[0].triggers["capacity_providers"] + ] } diff --git a/modules/cluster/README.md b/modules/cluster/README.md index a041d2c..430f639 100644 --- a/modules/cluster/README.md +++ b/modules/cluster/README.md @@ -135,13 +135,14 @@ module "ecs_cluster" { | Name | Version | |------|---------| | [terraform](#requirement\_terraform) | >= 1.5.7 | -| [aws](#requirement\_aws) | >= 6.14 | +| [aws](#requirement\_aws) | >= 6.15 | ## Providers | Name | Version | |------|---------| -| [aws](#provider\_aws) | >= 6.14 | +| [aws](#provider\_aws) | >= 6.15 | +| [time](#provider\_time) | n/a | ## Modules @@ -155,31 +156,80 @@ No modules. | [aws_ecs_capacity_provider.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/ecs_capacity_provider) | resource | | [aws_ecs_cluster.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/ecs_cluster) | resource | | [aws_ecs_cluster_capacity_providers.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/ecs_cluster_capacity_providers) | resource | +| [aws_iam_instance_profile.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_instance_profile) | resource | +| [aws_iam_policy.infrastructure](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_policy) | resource | +| [aws_iam_policy.node](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_policy) | resource | | [aws_iam_policy.task_exec](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_policy) | resource | +| [aws_iam_role.infrastructure](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role) | resource | +| [aws_iam_role.node](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role) | resource | | [aws_iam_role.task_exec](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role) | resource | +| [aws_iam_role_policy_attachment.infrastructure](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource | +| [aws_iam_role_policy_attachment.node](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource | +| [aws_iam_role_policy_attachment.node_additional](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 | | [aws_iam_role_policy_attachment.task_exec_additional](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource | +| [aws_security_group.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group) | resource | +| [aws_vpc_security_group_egress_rule.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/vpc_security_group_egress_rule) | resource | +| [aws_vpc_security_group_ingress_rule.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/vpc_security_group_ingress_rule) | resource | +| [time_sleep.this](https://registry.terraform.io/providers/hashicorp/time/latest/docs/resources/sleep) | resource | +| [aws_caller_identity.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/caller_identity) | data source | +| [aws_iam_policy_document.infrastructure](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source | +| [aws_iam_policy_document.infrastructure_assume](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source | +| [aws_iam_policy_document.node](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source | +| [aws_iam_policy_document.node_assume_role_policy](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source | | [aws_iam_policy_document.task_exec](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source | | [aws_iam_policy_document.task_exec_assume](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source | +| [aws_partition.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/partition) | data source | +| [aws_region.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/region) | data source | ## Inputs | 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 | +| [autoscaling\_capacity\_providers](#input\_autoscaling\_capacity\_providers) | [DEPRECATED - use `capacity_providers` instead] 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\_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` | `"30s"` | 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 | +| [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
}))
|
[
{
"name": "containerInsights",
"value": "enabled"
}
]
| no | | [tags](#input\_tags) | A map of tags to add to all resources | `map(string)` | `{}` | no | @@ -193,21 +243,32 @@ 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 |
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 | +| [autoscaling\_capacity\_providers](#output\_autoscaling\_capacity\_providers) | [DEPRECATED - use `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 6eabcc3..fa66bb2 100644 --- a/modules/cluster/main.tf +++ b/modules/cluster/main.tf @@ -1,3 +1,36 @@ +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, "") +} + +# This sleep resource is used to provide a timed gap between the cluster creation and the downstream dependencies +# that consume the outputs from here. Any of the values that are used as triggers can be used in dependencies +# to ensure that the downstream resources are created after both the cluster is ready and the sleep time has passed. +# This was primarily added to give addons that need to be configured BEFORE data plane compute resources +# enough time to create and configure themselves before the data plane compute resources are created. +resource "time_sleep" "this" { + count = var.create ? 1 : 0 + + create_duration = var.cluster_capacity_providers_wait_duration + + triggers = { + name = aws_ecs_cluster.this[0].name + } +} + ################################################################################ # Cluster ################################################################################ @@ -95,16 +128,27 @@ resource "aws_cloudwatch_log_group" "this" { # Cluster Capacity Providers ################################################################################ +locals { + # `managed_instances_provider` are automatically associated with clusters and + # will cause an error if you try to associate by passing to `capacity_providers` below. + auto_scaling_group_provider_names = local.capacity_providers != null ? [for k, v in local.capacity_providers : try(coalesce(v.name, k)) if v.auto_scaling_group_provider != null] : [] + managed_instances_provider_names = local.capacity_providers != null ? [for k, v in local.capacity_providers : try(coalesce(v.name, k)) if v.managed_instances_provider != null] : [] + default_capacity_provider_names = [for k, v in var.default_capacity_provider_strategy : try(coalesce(v.name, k))] + + # Grab all providers but ensure `managed_instances_provider` name's are excluded + cluster_capacity_providers = [for name in distinct(concat( + local.default_capacity_provider_names, + local.auto_scaling_group_provider_names + )) : name if !contains(local.managed_instances_provider_names, name)] +} + 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 = time_sleep.this[0].triggers["name"] + capacity_providers = local.cluster_capacity_providers # https://docs.aws.amazon.com/AmazonECS/latest/developerguide/cluster-capacity-providers.html#capacity-providers-considerations dynamic "default_capacity_provider_strategy" { @@ -113,7 +157,7 @@ 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) } } @@ -123,40 +167,221 @@ resource "aws_ecs_cluster_capacity_providers" "this" { } ################################################################################ -# Capacity Provider - Autoscaling Group(s) +# Capacity Provider ################################################################################ +locals { + managed_instances_enabled = anytrue([for k, v in local.capacity_providers : v.managed_instances_provider != null]) + + # TODO - embed the `autoscaling_capacity_providers` into a shape acceptable for + # `var.capacity_providers` so that it can be merged with the new `capacity_providers` + # for backward compatibility. Remove `autoscaling_capacity_providers` in the next major version. + capacity_providers = var.autoscaling_capacity_providers != null ? merge({ + for k, v in var.autoscaling_capacity_providers : k => { + auto_scaling_group_provider = { + autoscaling_group_arn = v.autoscaling_group_arn + managed_draining = try(v.managed_draining, null) + managed_scaling = try(v.managed_scaling, null) != null ? { + instance_warmup_period = try(v.managed_scaling.instance_warmup_period, null) + maximum_scaling_step_size = try(v.managed_scaling.maximum_scaling_step_size, null) + minimum_scaling_step_size = try(v.managed_scaling.minimum_scaling_step_size, null) + status = try(v.managed_scaling.status, null) + target_capacity = try(v.managed_scaling.target_capacity, null) + } : null + managed_termination_protection = try(v.managed_termination_protection, null) + } + managed_instances_provider = null + name = try(v.name, k) + tags = try(v.tags, {}) + } + }, var.capacity_providers != null ? var.capacity_providers : {}) : var.capacity_providers +} + resource "aws_ecs_capacity_provider" "this" { - for_each = var.create && var.autoscaling_capacity_providers != null ? var.autoscaling_capacity_providers : {} + for_each = var.create && local.capacity_providers != null ? local.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 = each.value.auto_scaling_group_arn + managed_draining = each.value.managed_draining - 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 + dynamic "managed_scaling" { + for_each = each.value.managed_scaling != null ? [each.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 + } } + + # 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" } + } - # 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 "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 + + 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, ) + + depends_on = [ + aws_iam_role_policy_attachment.infrastructure, + aws_iam_role_policy_attachment.node, + ] } ################################################################################ @@ -311,3 +536,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", "NotProvided") +} + +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 = var.node_iam_role_description + + 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, "NotProvided") +} + +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 = var.security_group_description + 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 4e05381..2c5ebfb 100644 --- a/modules/cluster/outputs.tf +++ b/modules/cluster/outputs.tf @@ -45,6 +45,11 @@ output "cluster_capacity_providers" { ################################################################################ output "autoscaling_capacity_providers" { + description = "[DEPRECATED - use `capacity_providers`] Map of autoscaling capacity providers created and their attributes" + value = aws_ecs_capacity_provider.this +} + +output "capacity_providers" { description = "Map of autoscaling capacity providers created and their attributes" value = aws_ecs_capacity_provider.this } @@ -54,17 +59,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 495e5dd..72612ad 100644 --- a/modules/cluster/variables.tf +++ b/modules/cluster/variables.tf @@ -121,8 +121,14 @@ variable "cloudwatch_log_group_tags" { # Capacity Providers ################################################################################ +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 = "30s" +} + variable "autoscaling_capacity_providers" { - description = "Map of autoscaling capacity provider definitions to create for the cluster" + description = "[DEPRECATED - use `capacity_providers` instead] Map of autoscaling capacity provider definitions to create for the cluster" type = map(object({ auto_scaling_group_arn = string managed_draining = optional(string, "ENABLED") @@ -140,6 +146,95 @@ variable "autoscaling_capacity_providers" { default = null } +variable "capacity_providers" { + description = "Map of capacity provider definitions to create for the cluster" + type = 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), {}) + })) + default = null +} + variable "default_capacity_provider_strategy" { description = "Map of default capacity provider strategy definitions to use for the cluster" type = map(object({ @@ -247,3 +342,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 0e90b8c..e1ac31c 100644 --- a/modules/cluster/versions.tf +++ b/modules/cluster/versions.tf @@ -4,7 +4,7 @@ terraform { required_providers { aws = { source = "hashicorp/aws" - version = ">= 6.14" + version = ">= 6.15" } } } diff --git a/modules/container-definition/README.md b/modules/container-definition/README.md index b5b994e..c5fb3b9 100644 --- a/modules/container-definition/README.md +++ b/modules/container-definition/README.md @@ -116,13 +116,13 @@ module "example_ecs_container_definition" { | Name | Version | |------|---------| | [terraform](#requirement\_terraform) | >= 1.5.7 | -| [aws](#requirement\_aws) | >= 6.14 | +| [aws](#requirement\_aws) | >= 6.15 | ## Providers | Name | Version | |------|---------| -| [aws](#provider\_aws) | >= 6.14 | +| [aws](#provider\_aws) | >= 6.15 | ## Modules diff --git a/modules/container-definition/versions.tf b/modules/container-definition/versions.tf index 0e90b8c..e1ac31c 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.14" + version = ">= 6.15" } } } diff --git a/modules/service/README.md b/modules/service/README.md index 8891e50..1ea80bc 100644 --- a/modules/service/README.md +++ b/modules/service/README.md @@ -170,13 +170,13 @@ module "ecs_service" { | Name | Version | |------|---------| | [terraform](#requirement\_terraform) | >= 1.5.7 | -| [aws](#requirement\_aws) | >= 6.14 | +| [aws](#requirement\_aws) | >= 6.15 | ## Providers | Name | Version | |------|---------| -| [aws](#provider\_aws) | >= 6.14 | +| [aws](#provider\_aws) | >= 6.15 | ## Modules @@ -294,7 +294,7 @@ module "ecs_service" { | [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)` |
[
"FARGATE"
]
| 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 |
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 f467f56..f5482a4 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) } } @@ -383,7 +383,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) } } @@ -1356,7 +1356,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) } } @@ -1439,7 +1439,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) } } diff --git a/modules/service/variables.tf b/modules/service/variables.tf index 42afafc..20c2e03 100644 --- a/modules/service/variables.tf +++ b/modules/service/variables.tf @@ -674,7 +674,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 @@ -1273,7 +1273,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 0e90b8c..e1ac31c 100644 --- a/modules/service/versions.tf +++ b/modules/service/versions.tf @@ -4,7 +4,7 @@ terraform { required_providers { aws = { source = "hashicorp/aws" - version = ">= 6.14" + version = ">= 6.15" } } } diff --git a/outputs.tf b/outputs.tf index 6e462c6..37a9d60 100644 --- a/outputs.tf +++ b/outputs.tf @@ -33,10 +33,15 @@ output "cluster_capacity_providers" { } output "autoscaling_capacity_providers" { - description = "Map of autoscaling capacity providers created and their attributes" + description = "[DEPRECATED - use `capacity_providers`] Map of autoscaling capacity providers created and their attributes" value = module.cluster.autoscaling_capacity_providers } +output "capacity_providers" { + description = "Map of autoscaling capacity providers created and their attributes" + value = module.cluster.capacity_providers +} + output "task_exec_iam_role_name" { description = "Task execution IAM role name" value = module.cluster.task_exec_iam_role_name @@ -52,6 +57,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 b537cb9..f9beff9 100644 --- a/variables.tf +++ b/variables.tf @@ -16,6 +16,12 @@ variable "tags" { default = {} } +variable "wait_duration" { + description = "Duration to wait between ECS cluster and ECS service (to give enough time for capacity providers to reach ACTIVE state)" + type = string + default = "60s" +} + ################################################################################ # Cluster ################################################################################ @@ -127,7 +133,7 @@ variable "cloudwatch_log_group_tags" { ################################################################################ variable "autoscaling_capacity_providers" { - description = "Map of autoscaling capacity provider definitions to create for the cluster" + description = "[DEPRECATED - use `capacity_providers` instead] Map of autoscaling capacity provider definitions to create for the cluster" type = map(object({ auto_scaling_group_arn = string managed_draining = optional(string, "ENABLED") @@ -145,6 +151,95 @@ variable "autoscaling_capacity_providers" { default = null } +variable "capacity_providers" { + description = "Map of capacity provider definitions to create for the cluster" + type = 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), {}) + })) + default = null +} + variable "default_capacity_provider_strategy" { description = "Map of default capacity provider strategy definitions to use for the cluster" type = map(object({ @@ -252,6 +347,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) ################################################################################ diff --git a/versions.tf b/versions.tf index 0e90b8c..e1ac31c 100644 --- a/versions.tf +++ b/versions.tf @@ -4,7 +4,7 @@ terraform { required_providers { aws = { source = "hashicorp/aws" - version = ">= 6.14" + version = ">= 6.15" } } } diff --git a/wrappers/cluster/main.tf b/wrappers/cluster/main.tf index bbd27c5..9d49577 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, {}) + 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_wait_duration = try(each.value.cluster_capacity_providers_wait_duration, var.defaults.cluster_capacity_providers_wait_duration, "30s") configuration = try(each.value.configuration, var.defaults.configuration, { execute_command_configuration = { log_configuration = { @@ -16,14 +18,53 @@ 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, {}) + 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 +82,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 0e90b8c..e1ac31c 100644 --- a/wrappers/cluster/versions.tf +++ b/wrappers/cluster/versions.tf @@ -4,7 +4,7 @@ terraform { required_providers { aws = { source = "hashicorp/aws" - version = ">= 6.14" + version = ">= 6.15" } } } diff --git a/wrappers/container-definition/versions.tf b/wrappers/container-definition/versions.tf index 0e90b8c..e1ac31c 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.14" + version = ">= 6.15" } } } diff --git a/wrappers/main.tf b/wrappers/main.tf index 13659ca..ce6d732 100644 --- a/wrappers/main.tf +++ b/wrappers/main.tf @@ -4,6 +4,7 @@ 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) @@ -24,13 +25,52 @@ 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) + 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 +83,6 @@ 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) + wait_duration = try(each.value.wait_duration, var.defaults.wait_duration, "60s") } diff --git a/wrappers/service/versions.tf b/wrappers/service/versions.tf index 0e90b8c..e1ac31c 100644 --- a/wrappers/service/versions.tf +++ b/wrappers/service/versions.tf @@ -4,7 +4,7 @@ terraform { required_providers { aws = { source = "hashicorp/aws" - version = ">= 6.14" + version = ">= 6.15" } } } diff --git a/wrappers/versions.tf b/wrappers/versions.tf index 0e90b8c..e1ac31c 100644 --- a/wrappers/versions.tf +++ b/wrappers/versions.tf @@ -4,7 +4,7 @@ terraform { required_providers { aws = { source = "hashicorp/aws" - version = ">= 6.14" + version = ">= 6.15" } } }