From fac62556953b3e1453eafb5d9c20d1ddd115bb43 Mon Sep 17 00:00:00 2001 From: Bryant Biggs Date: Thu, 2 Jun 2022 12:39:14 -0400 Subject: [PATCH 1/7] feat!: Upgrade module to include capacity providers, cloudwatch log group, and minimum supported versions --- .pre-commit-config.yaml | 4 +- README.md | 34 +++-- UPGRADE-4.0.md | 50 +++++++ examples/complete-ecs/README.md | 14 +- examples/complete-ecs/main.tf | 31 ++--- examples/complete-ecs/outputs.tf | 35 +++++ .../service-hello-world/versions.tf | 7 +- examples/complete-ecs/versions.tf | 4 +- examples/fargate/README.md | 60 +++++++++ examples/fargate/main.tf | 83 ++++++++++++ examples/fargate/outputs.tf | 35 +++++ examples/fargate/variables.tf | 0 examples/fargate/versions.tf | 10 ++ main.tf | 123 ++++++++++++++++-- outputs.tf | 40 ++++-- variables.tf | 89 ++++++++++--- versions.tf | 4 +- 17 files changed, 537 insertions(+), 86 deletions(-) create mode 100644 UPGRADE-4.0.md create mode 100644 examples/fargate/README.md create mode 100644 examples/fargate/main.tf create mode 100644 examples/fargate/outputs.tf create mode 100644 examples/fargate/variables.tf create mode 100644 examples/fargate/versions.tf diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 093121e..b3ff80e 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,6 +1,6 @@ repos: - repo: https://github.com/antonbabenko/pre-commit-terraform - rev: v1.62.3 + rev: v1.72.1 hooks: - id: terraform_fmt - id: terraform_validate @@ -23,7 +23,7 @@ repos: - '--args=--only=terraform_standard_module_structure' - '--args=--only=terraform_workspace_remote' - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.1.0 + rev: v4.2.0 hooks: - id: check-merge-conflict - id: end-of-file-fixer diff --git a/README.md b/README.md index 24a9a70..6ee716e 100644 --- a/README.md +++ b/README.md @@ -57,14 +57,14 @@ module "ecs" { | Name | Version | |------|---------| -| [terraform](#requirement\_terraform) | >= 0.13.1 | -| [aws](#requirement\_aws) | >= 3.74 | +| [terraform](#requirement\_terraform) | >= 1.0 | +| [aws](#requirement\_aws) | >= 4.6 | ## Providers | Name | Version | |------|---------| -| [aws](#provider\_aws) | >= 3.74 | +| [aws](#provider\_aws) | >= 4.6 | ## Modules @@ -74,6 +74,8 @@ No modules. | Name | Type | |------|------| +| [aws_cloudwatch_log_group.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudwatch_log_group) | resource | +| [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 | @@ -81,20 +83,28 @@ No modules. | Name | Description | Type | Default | Required | |------|-------------|------|---------|:--------:| -| [capacity\_providers](#input\_capacity\_providers) | List of short names of one or more capacity providers to associate with the cluster. Valid values also include FARGATE and FARGATE\_SPOT. | `list(string)` | `[]` | no | -| [container\_insights](#input\_container\_insights) | Controls if ECS Cluster has container insights enabled | `bool` | `false` | no | -| [create\_ecs](#input\_create\_ecs) | Controls if ECS should be created | `bool` | `true` | no | -| [default\_capacity\_provider\_strategy](#input\_default\_capacity\_provider\_strategy) | The capacity provider strategy to use by default for the cluster. Can be one or more. | `list(map(any))` | `[]` | no | -| [name](#input\_name) | Name to be used on all the resources as identifier, also the name of the ECS cluster | `string` | `null` | no | -| [tags](#input\_tags) | A map of tags to add to ECS Cluster | `map(string)` | `{}` | no | +| [capacity\_providers](#input\_capacity\_providers) | Map of capacity provider definitons to create | `any` | `{}` | 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) | Name of the cloudwatch log group created. If not provided, defaults to `/aws/ecs/` | `string` | `null` | no | +| [cloudwatch\_log\_group\_retention\_in\_days](#input\_cloudwatch\_log\_group\_retention\_in\_days) | Number of days to retain log events. Default retention - 30 days | `number` | `30` | no | +| [cluster\_capacity\_providers](#input\_cluster\_capacity\_providers) | The capacity providers to use for the cluster | `list(string)` | `[]` | no | +| [cluster\_configuration](#input\_cluster\_configuration) | The execute command configuration for the cluster | `any` | `{}` | no | +| [cluster\_default\_capacity\_provider\_strategy](#input\_cluster\_default\_capacity\_provider\_strategy) | The default capacity provider strategy to use for the cluster | `list(map(string))` | `[]` | no | +| [cluster\_name](#input\_cluster\_name) | Name of the cluster (up to 255 letters, numbers, hyphens, and underscores) | `string` | `""` | no | +| [cluster\_settings](#input\_cluster\_settings) | Configuration block(s) with cluster settings. For example, this can be used to enable CloudWatch Container Insights for a cluster | `map(string)` |
{
"name": "containerInsights",
"value": "enabled"
}
| 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 | +| [tags](#input\_tags) | A map of tags to add to all resources | `map(string)` | `{}` | no | ## Outputs | Name | Description | |------|-------------| -| [ecs\_cluster\_arn](#output\_ecs\_cluster\_arn) | ARN of the ECS Cluster | -| [ecs\_cluster\_id](#output\_ecs\_cluster\_id) | ID of the ECS Cluster | -| [ecs\_cluster\_name](#output\_ecs\_cluster\_name) | The name of the ECS cluster | +| [capacity\_providers](#output\_capacity\_providers) | Map of 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\_id](#output\_cluster\_id) | ID that identifies the cluster | ## Authors diff --git a/UPGRADE-4.0.md b/UPGRADE-4.0.md new file mode 100644 index 0000000..81bbfc6 --- /dev/null +++ b/UPGRADE-4.0.md @@ -0,0 +1,50 @@ +# Upgrade from v3.x to v4.x + +Please consult the `examples` directory for reference example configurations. If you find a bug, please open an issue with supporting configuration to reproduce. + +## List of backwards incompatible changes + +- Minimum supported version of Terraform AWS provider updated to v4.6 to support latest resources +- Minimum supported version of Terraform updated to v1.0 + +## Additional changes + +### Added + +- + +### Modified + +- + +### Removed + +- + +### Variable and output changes + +1. Removed variables: + + - + +2. Renamed variables: + + - + +3. Added variables: + + - + +4. Removed outputs: + + - + +5. Renamed outputs: + + - + +6. Added outputs: + + - + +## Upgrade Migrations diff --git a/examples/complete-ecs/README.md b/examples/complete-ecs/README.md index f17a3f4..cc92bf9 100644 --- a/examples/complete-ecs/README.md +++ b/examples/complete-ecs/README.md @@ -43,14 +43,14 @@ Current version creates an high-available VPC with instances that are attached t | Name | Version | |------|---------| -| [terraform](#requirement\_terraform) | >= 0.13.1 | -| [aws](#requirement\_aws) | >= 3.74 | +| [terraform](#requirement\_terraform) | >= 1.0 | +| [aws](#requirement\_aws) | >= 4.6 | ## Providers | Name | Version | |------|---------| -| [aws](#provider\_aws) | >= 3.74 | +| [aws](#provider\_aws) | >= 4.6 | ## Modules @@ -77,5 +77,11 @@ No inputs. ## Outputs -No outputs. +| Name | Description | +|------|-------------| +| [capacity\_providers](#output\_capacity\_providers) | Map of 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\_id](#output\_cluster\_id) | ID that identifies the cluster | diff --git a/examples/complete-ecs/main.tf b/examples/complete-ecs/main.tf index eb6aa37..ac32822 100644 --- a/examples/complete-ecs/main.tf +++ b/examples/complete-ecs/main.tf @@ -34,15 +34,13 @@ module "vpc" { module "ecs" { source = "../../" - name = local.name - container_insights = true + cluster_name = local.name - capacity_providers = ["FARGATE", "FARGATE_SPOT", aws_ecs_capacity_provider.prov1.name] - - default_capacity_provider_strategy = [{ - capacity_provider = aws_ecs_capacity_provider.prov1.name # "FARGATE_SPOT" - weight = "1" - }] + # capacity_providers = [aws_ecs_capacity_provider.prov1.name] + # cluster_default_capacity_provider_strategy = [{ + # capacity_provider = aws_ecs_capacity_provider.prov1.name # "FARGATE_SPOT" + # weight = "1" + # }] tags = { Environment = local.environment @@ -72,7 +70,7 @@ resource "aws_ecs_capacity_provider" "prov1" { module "hello_world" { source = "./service-hello-world" - cluster_id = module.ecs.ecs_cluster_id + cluster_id = module.ecs.cluster_id } #----- ECS Resources-------- @@ -121,18 +119,7 @@ module "asg" { desired_capacity = 0 # we don't need them for the example wait_for_capacity_timeout = 0 - tags = [ - { - key = "Environment" - value = local.environment - propagate_at_launch = true - }, - { - key = "Cluster" - value = local.name - propagate_at_launch = true - }, - ] + # tags = local.tags } ################### @@ -142,5 +129,5 @@ module "asg" { module "disabled_ecs" { source = "../../" - create_ecs = false + create = false } diff --git a/examples/complete-ecs/outputs.tf b/examples/complete-ecs/outputs.tf index e69de29..51e778c 100644 --- a/examples/complete-ecs/outputs.tf +++ b/examples/complete-ecs/outputs.tf @@ -0,0 +1,35 @@ +################################################################################ +# 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 +} + +################################################################################ +# CloudWatch Log Group +################################################################################ + +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 +} +################################################################################ +# Capacity Provider +################################################################################ + +output "capacity_providers" { + description = "Map of capacity providers created and their attributes" + value = module.ecs.capacity_providers +} diff --git a/examples/complete-ecs/service-hello-world/versions.tf b/examples/complete-ecs/service-hello-world/versions.tf index 5641dfb..35402be 100644 --- a/examples/complete-ecs/service-hello-world/versions.tf +++ b/examples/complete-ecs/service-hello-world/versions.tf @@ -1,7 +1,10 @@ terraform { - required_version = ">= 0.12.6" + required_version = ">= 1.0" required_providers { - aws = ">= 2.0" + aws = { + source = "hashicorp/aws" + version = ">= 4.6" + } } } diff --git a/examples/complete-ecs/versions.tf b/examples/complete-ecs/versions.tf index 538b915..35402be 100644 --- a/examples/complete-ecs/versions.tf +++ b/examples/complete-ecs/versions.tf @@ -1,10 +1,10 @@ terraform { - required_version = ">= 0.13.1" + required_version = ">= 1.0" required_providers { aws = { source = "hashicorp/aws" - version = ">= 3.74" + version = ">= 4.6" } } } diff --git a/examples/fargate/README.md b/examples/fargate/README.md new file mode 100644 index 0000000..e76118d --- /dev/null +++ b/examples/fargate/README.md @@ -0,0 +1,60 @@ +# Complete AWS ECS Example + +Configuration in this directory creates: + +- + +## 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.0 | +| [aws](#requirement\_aws) | >= 4.6 | + +## Providers + +| Name | Version | +|------|---------| +| [aws](#provider\_aws) | >= 4.6 | + +## Modules + +| Name | Source | Version | +|------|--------|---------| +| [ecs](#module\_ecs) | ../.. | n/a | +| [ecs\_disabled](#module\_ecs\_disabled) | ../.. | n/a | +| [vpc](#module\_vpc) | terraform-aws-modules/vpc/aws | ~> 3.0 | + +## Resources + +| Name | Type | +|------|------| +| [aws_kms_key.example](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/kms_key) | resource | + +## Inputs + +No inputs. + +## Outputs + +| Name | Description | +|------|-------------| +| [capacity\_providers](#output\_capacity\_providers) | Map of 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\_id](#output\_cluster\_id) | ID that identifies the cluster | + diff --git a/examples/fargate/main.tf b/examples/fargate/main.tf new file mode 100644 index 0000000..64200bd --- /dev/null +++ b/examples/fargate/main.tf @@ -0,0 +1,83 @@ +provider "aws" { + region = local.region +} + +locals { + region = "us-east-1" + name = "ecs-ex-${replace(basename(path.cwd), "_", "-")}" + + tags = { + Name = local.name + Example = local.name + Repository = "https://github.com/terraform-aws-modules/terraform-aws-ecs" + } +} + +################################################################################ +# Ecs Module +################################################################################ + +module "ecs_disabled" { + source = "../.." + + create = false +} + +module "ecs" { + source = "../.." + + cluster_name = local.name + cluster_configuration = { + execute_command_configuration = { + kms_key_id = aws_kms_key.example.arn + logging = "OVERRIDE" + } + } + + # Capacity provider + cluster_capacity_providers = ["FARGATE", "FARGATE_SPOT"] + cluster_default_capacity_provider_strategy = [ + { + + capacity_provider = "FARGATE" + weight = 50 + }, + { + capacity_provider = "FARGATE_SPOT" + weight = 50 + } + ] + + tags = local.tags +} + +################################################################################ +# Supporting Resources +################################################################################ + +module "vpc" { + source = "terraform-aws-modules/vpc/aws" + version = "~> 3.0" + + create_vpc = false + + name = local.name + cidr = "10.99.0.0/18" + + azs = ["${local.region}a", "${local.region}b", "${local.region}c"] + public_subnets = ["10.99.0.0/24", "10.99.1.0/24", "10.99.2.0/24"] + private_subnets = ["10.99.3.0/24", "10.99.4.0/24", "10.99.5.0/24"] + + enable_nat_gateway = true + single_nat_gateway = true + map_public_ip_on_launch = false + + tags = local.tags +} + +resource "aws_kms_key" "example" { + description = local.name + deletion_window_in_days = 7 + + tags = local.tags +} diff --git a/examples/fargate/outputs.tf b/examples/fargate/outputs.tf new file mode 100644 index 0000000..51e778c --- /dev/null +++ b/examples/fargate/outputs.tf @@ -0,0 +1,35 @@ +################################################################################ +# 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 +} + +################################################################################ +# CloudWatch Log Group +################################################################################ + +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 +} +################################################################################ +# Capacity Provider +################################################################################ + +output "capacity_providers" { + description = "Map of capacity providers created and their attributes" + value = module.ecs.capacity_providers +} diff --git a/examples/fargate/variables.tf b/examples/fargate/variables.tf new file mode 100644 index 0000000..e69de29 diff --git a/examples/fargate/versions.tf b/examples/fargate/versions.tf new file mode 100644 index 0000000..35402be --- /dev/null +++ b/examples/fargate/versions.tf @@ -0,0 +1,10 @@ +terraform { + required_version = ">= 1.0" + + required_providers { + aws = { + source = "hashicorp/aws" + version = ">= 4.6" + } + } +} diff --git a/main.tf b/main.tf index 76badad..ba90404 100644 --- a/main.tf +++ b/main.tf @@ -1,31 +1,126 @@ +################################################################################ +# Cluster +################################################################################ + +locals { + # Used to default to logs enabled and sent to cloudwatch group created by module + cluster_configuration = merge( + { + execute_command_configuration = { + log_configuration = { + cloud_watch_log_group_name = var.create_cloudwatch_log_group ? aws_cloudwatch_log_group.this[0].name : null + } + } + }, + var.cluster_configuration, + ) +} + resource "aws_ecs_cluster" "this" { - count = var.create_ecs ? 1 : 0 + count = var.create ? 1 : 0 + + name = var.cluster_name + + + dynamic "configuration" { + for_each = [local.cluster_configuration] + + content { + dynamic "execute_command_configuration" { + for_each = [configuration.value.execute_command_configuration] + + content { + kms_key_id = try(execute_command_configuration.value.kms_key_id, null) + logging = try(execute_command_configuration.value.logging, "DEFAULT") - name = var.name + dynamic "log_configuration" { + for_each = [execute_command_configuration.value.log_configuration] - setting { - name = "containerInsights" - value = var.container_insights ? "enabled" : "disabled" + content { + cloud_watch_encryption_enabled = try(log_configuration.value.cloud_watch_encryption_enabled, null) + cloud_watch_log_group_name = try(log_configuration.value.cloud_watch_log_group_name, null) + s3_bucket_name = try(log_configuration.value.s3_bucket_name, null) + s3_bucket_encryption_enabled = try(log_configuration.value.s3_bucket_encryption_enabled, null) + s3_key_prefix = try(log_configuration.value.s3_key_prefix, null) + } + } + } + } + } + } + + dynamic "setting" { + for_each = length(var.cluster_settings) > 0 ? [var.cluster_settings] : [] + + content { + name = try(setting.value.name, "containerInsights") + value = setting.value.value + } } tags = var.tags } -resource "aws_ecs_cluster_capacity_providers" "this" { - count = var.create_ecs ? 1 : 0 +################################################################################ +# CloudWatch Log Group +################################################################################ - cluster_name = aws_ecs_cluster.this[0].name +resource "aws_cloudwatch_log_group" "this" { + count = var.create && var.create_cloudwatch_log_group ? 1 : 0 - capacity_providers = var.capacity_providers + name = coalesce(var.cloudwatch_log_group_name, "/aws/ecs/${var.cluster_name}") + retention_in_days = var.cloudwatch_log_group_retention_in_days + kms_key_id = var.cloudwatch_log_group_kms_key_id + + tags = var.tags +} + +################################################################################ +# Cluster Capacity Providers - Fargate +################################################################################ + +resource "aws_ecs_cluster_capacity_providers" "this" { + count = var.create ? 1 : 0 + + cluster_name = aws_ecs_cluster.this[0].name + capacity_providers = var.cluster_capacity_providers dynamic "default_capacity_provider_strategy" { - for_each = var.default_capacity_provider_strategy - iterator = strategy + for_each = var.cluster_default_capacity_provider_strategy content { - capacity_provider = strategy.value["capacity_provider"] - weight = lookup(strategy.value, "weight", null) - base = lookup(strategy.value, "base", null) + capacity_provider = default_capacity_provider_strategy.value.capacity_provider + base = try(default_capacity_provider_strategy.value.base, null) + weight = try(default_capacity_provider_strategy.value.weight, null) + } + } +} + +################################################################################ +# Capacity Provider - Autoscaling Group(s) +################################################################################ + +resource "aws_ecs_capacity_provider" "this" { + for_each = { for k, v in var.capacity_providers : k => v if var.create } + + name = try(each.value.name, each.key) + + auto_scaling_group_provider { + auto_scaling_group_arn = each.value.auto_scaling_group_arn + managed_termination_protection = each.value.managed_termination_protection + + dynamic "managed_scaling" { + for_each = try([each.value.managed_scaling], []) + + content { + instance_warmup_period = try(managed_scaling.value.instance_warmup_period, null) + maximum_scaling_step_size = try(managed_scaling.value.maximum_scaling_step_size, null) + minimum_scaling_step_size = try(managed_scaling.value.minimum_scaling_step_size, null) + status = try(managed_scaling.value.status, null) + target_capacity = try(managed_scaling.value.target_capacity, null) + } } } + + tags = merge(var.tags, try(each.value.tags, {})) } diff --git a/outputs.tf b/outputs.tf index 3c2c7fb..c762494 100644 --- a/outputs.tf +++ b/outputs.tf @@ -1,14 +1,36 @@ -output "ecs_cluster_id" { - description = "ID of the ECS Cluster" - value = concat(aws_ecs_cluster.this.*.id, [""])[0] +################################################################################ +# Cluster +################################################################################ + +output "cluster_arn" { + description = "ARN that identifies the cluster" + value = try(aws_ecs_cluster.this[0].arn, null) +} + +output "cluster_id" { + description = "ID that identifies the cluster" + value = try(aws_ecs_cluster.this[0].id, null) } -output "ecs_cluster_arn" { - description = "ARN of the ECS Cluster" - value = concat(aws_ecs_cluster.this.*.arn, [""])[0] +################################################################################ +# CloudWatch Log Group +################################################################################ + +output "cloudwatch_log_group_name" { + description = "Name of cloudwatch log group created" + value = try(aws_cloudwatch_log_group.this[0].name, null) } -output "ecs_cluster_name" { - description = "The name of the ECS cluster" - value = var.name +output "cloudwatch_log_group_arn" { + description = "Arn of cloudwatch log group created" + value = try(aws_cloudwatch_log_group.this[0].arn, null) +} + +################################################################################ +# Capacity Provider - Autoscaling Group(s) +################################################################################ + +output "capacity_providers" { + description = "Map of capacity providers created and their attributes" + value = aws_ecs_capacity_provider.this } diff --git a/variables.tf b/variables.tf index 3da768d..50e2f46 100644 --- a/variables.tf +++ b/variables.tf @@ -1,35 +1,90 @@ -variable "create_ecs" { - description = "Controls if ECS should be created" +variable "create" { + description = "Determines whether resources will be created (affects all resources)" type = bool default = true } -variable "name" { - description = "Name to be used on all the resources as identifier, also the name of the ECS cluster" +variable "tags" { + description = "A map of tags to add to all resources" + type = map(string) + default = {} +} + +################################################################################ +# Cluster +################################################################################ + +variable "cluster_name" { + description = "Name of the cluster (up to 255 letters, numbers, hyphens, and underscores)" + type = string + default = "" +} + +variable "cluster_configuration" { + description = "The execute command configuration for the cluster" + type = any + default = {} +} + +variable "cluster_settings" { + description = "Configuration block(s) with cluster settings. For example, this can be used to enable CloudWatch Container Insights for a cluster" + type = map(string) + default = { + name = "containerInsights" + value = "enabled" + } +} + +################################################################################ +# CloudWatch Log Group +################################################################################ + +variable "create_cloudwatch_log_group" { + description = "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" + type = bool + default = true +} + +variable "cloudwatch_log_group_name" { + description = "Name of the cloudwatch log group created. If not provided, defaults to `/aws/ecs/`" type = string default = null } -variable "capacity_providers" { - description = "List of short names of one or more capacity providers to associate with the cluster. Valid values also include FARGATE and FARGATE_SPOT." +variable "cloudwatch_log_group_retention_in_days" { + description = "Number of days to retain log events. Default retention - 30 days" + type = number + default = 30 +} + +variable "cloudwatch_log_group_kms_key_id" { + description = "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)" + type = string + default = null +} + +################################################################################ +# Cluster Capacity Providers +################################################################################ + +variable "cluster_capacity_providers" { + description = "The capacity providers to use for the cluster" type = list(string) default = [] } -variable "default_capacity_provider_strategy" { - description = "The capacity provider strategy to use by default for the cluster. Can be one or more." - type = list(map(any)) +variable "cluster_default_capacity_provider_strategy" { + description = "The default capacity provider strategy to use for the cluster" + type = list(map(string)) default = [] } -variable "container_insights" { - description = "Controls if ECS Cluster has container insights enabled" - type = bool - default = false -} +################################################################################ +# Capacity Provider +################################################################################ -variable "tags" { - description = "A map of tags to add to ECS Cluster" - type = map(string) +variable "capacity_providers" { + description = "Map of capacity provider definitons to create" + type = any default = {} } diff --git a/versions.tf b/versions.tf index 538b915..35402be 100644 --- a/versions.tf +++ b/versions.tf @@ -1,10 +1,10 @@ terraform { - required_version = ">= 0.13.1" + required_version = ">= 1.0" required_providers { aws = { source = "hashicorp/aws" - version = ">= 3.74" + version = ">= 4.6" } } } From 4b34b8e267cc05878b2a704c231ed7d932eede36 Mon Sep 17 00:00:00 2001 From: Bryant Biggs Date: Fri, 3 Jun 2022 15:52:30 -0400 Subject: [PATCH 2/7] refactor: Update examples to utilize latest changes, setup new `service` moduel to come --- README.md | 31 +-- examples/complete-ecs/README.md | 87 -------- examples/complete-ecs/main.tf | 133 ------------ examples/complete-ecs/templates/user-data.sh | 10 - examples/ec2/README.md | 65 ++++++ examples/ec2/main.tf | 201 ++++++++++++++++++ examples/{complete-ecs => ec2}/outputs.tf | 12 +- .../service-hello-world/main.tf | 14 +- .../service-hello-world/outputs.tf | 0 .../service-hello-world/variables.tf | 0 .../service-hello-world/versions.tf | 0 examples/{complete-ecs => ec2}/variables.tf | 0 examples/{complete-ecs => ec2}/versions.tf | 0 examples/fargate/README.md | 10 +- examples/fargate/main.tf | 60 ++---- examples/fargate/outputs.tf | 12 +- main.tf | 60 ++---- modules/ecs-instance-profile/README.md | 53 ----- modules/ecs-instance-profile/main.tf | 47 ---- modules/ecs-instance-profile/outputs.tf | 14 -- modules/ecs-instance-profile/variables.tf | 16 -- modules/service/README.md | 66 ++++++ modules/service/main.tf | 1 + modules/service/outputs.tf | 0 modules/service/variables.tf | 0 .../versions.tf | 4 +- outputs.tf | 13 +- variables.tf | 38 +--- 28 files changed, 417 insertions(+), 530 deletions(-) delete mode 100644 examples/complete-ecs/README.md delete mode 100644 examples/complete-ecs/main.tf delete mode 100644 examples/complete-ecs/templates/user-data.sh create mode 100644 examples/ec2/README.md create mode 100644 examples/ec2/main.tf rename examples/{complete-ecs => ec2}/outputs.tf (74%) rename examples/{complete-ecs => ec2}/service-hello-world/main.tf (60%) rename examples/{complete-ecs => ec2}/service-hello-world/outputs.tf (100%) rename examples/{complete-ecs => ec2}/service-hello-world/variables.tf (100%) rename examples/{complete-ecs => ec2}/service-hello-world/versions.tf (100%) rename examples/{complete-ecs => ec2}/variables.tf (100%) rename examples/{complete-ecs => ec2}/versions.tf (100%) delete mode 100644 modules/ecs-instance-profile/README.md delete mode 100644 modules/ecs-instance-profile/main.tf delete mode 100644 modules/ecs-instance-profile/outputs.tf delete mode 100644 modules/ecs-instance-profile/variables.tf create mode 100644 modules/service/README.md create mode 100644 modules/service/main.tf create mode 100644 modules/service/outputs.tf create mode 100644 modules/service/variables.tf rename modules/{ecs-instance-profile => service}/versions.tf (60%) diff --git a/README.md b/README.md index 6ee716e..89e2de5 100644 --- a/README.md +++ b/README.md @@ -2,12 +2,9 @@ Terraform module which creates ECS resources on AWS. -This module focuses purely on ECS and nothing else. Therefore only these resources can be created with this module: +## Available Features -- [ECS](https://www.terraform.io/docs/providers/aws/r/ecs_cluster.html) -- [IAM](https://www.terraform.io/docs/providers/aws/r/iam_instance_profile.html) - -However, having said the above to have a proper ECS cluster up and running multiple resources are needed. In most cases creating these resources is heavily opinionated and or context-bound. That is why this module does not create these resources. But you still need them to have a production ready environment. Therefore the example area shows how to create everything needed for a production environment. +- ECS cluster w/ support for EC2 AutoScaling Group and/or Fargate provisioners ## Usage @@ -33,24 +30,25 @@ module "ecs" { } ``` -## Conditional creation +## Conditional Creation -Sometimes you need to have a way to create ECS resources conditionally but Terraform does not allow to use `count` inside `module` block, so the solution is to specify argument `create_ecs`. +The following values are provided to toggle on/off creation of the associated resources as desired: ```hcl -# ECS cluster will not be created module "ecs" { source = "terraform-aws-modules/ecs/aws" - version = "~> 2.0" - create_ecs = false + # Disable creation of cluster and all resources + create = false + # ... omitted } ``` ## Examples -- [Complete ECS](https://github.com/terraform-aws-modules/terraform-aws-ecs/tree/master/examples/complete-ecs) +- [ECS Cluster w/ EC2 Autoscaling](https://github.com/terraform-aws-modules/terraform-aws-ecs/tree/master/examples/ec2) +- [ECS Clusters w/ Fargate](https://github.com/terraform-aws-modules/terraform-aws-ecs/tree/master/examples/fargate) ## Requirements @@ -74,7 +72,6 @@ No modules. | Name | Type | |------|------| -| [aws_cloudwatch_log_group.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudwatch_log_group) | resource | | [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 | @@ -84,16 +81,11 @@ No modules. | Name | Description | Type | Default | Required | |------|-------------|------|---------|:--------:| | [capacity\_providers](#input\_capacity\_providers) | Map of capacity provider definitons to create | `any` | `{}` | 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) | Name of the cloudwatch log group created. If not provided, defaults to `/aws/ecs/` | `string` | `null` | no | -| [cloudwatch\_log\_group\_retention\_in\_days](#input\_cloudwatch\_log\_group\_retention\_in\_days) | Number of days to retain log events. Default retention - 30 days | `number` | `30` | no | -| [cluster\_capacity\_providers](#input\_cluster\_capacity\_providers) | The capacity providers to use for the cluster | `list(string)` | `[]` | no | +| [cluster\_capacity\_providers](#input\_cluster\_capacity\_providers) | The capacity providers to use for the cluster | `any` | `{}` | no | | [cluster\_configuration](#input\_cluster\_configuration) | The execute command configuration for the cluster | `any` | `{}` | no | -| [cluster\_default\_capacity\_provider\_strategy](#input\_cluster\_default\_capacity\_provider\_strategy) | The default capacity provider strategy to use for the cluster | `list(map(string))` | `[]` | no | | [cluster\_name](#input\_cluster\_name) | Name of the cluster (up to 255 letters, numbers, hyphens, and underscores) | `string` | `""` | no | | [cluster\_settings](#input\_cluster\_settings) | Configuration block(s) with cluster settings. For example, this can be used to enable CloudWatch Container Insights for a cluster | `map(string)` |
{
"name": "containerInsights",
"value": "enabled"
}
| 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 | | [tags](#input\_tags) | A map of tags to add to all resources | `map(string)` | `{}` | no | ## Outputs @@ -101,9 +93,8 @@ No modules. | Name | Description | |------|-------------| | [capacity\_providers](#output\_capacity\_providers) | Map of 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 | diff --git a/examples/complete-ecs/README.md b/examples/complete-ecs/README.md deleted file mode 100644 index cc92bf9..0000000 --- a/examples/complete-ecs/README.md +++ /dev/null @@ -1,87 +0,0 @@ -# Complete ECS - -This example uses only verified Terraform modules to create all resources that are needed for an ECS cluster that is sufficient for staging or production environment. - -While this example is still in the early stage there are other repositories that show how to create an ECS cluster: - -* -* -* -* - -## TODO - -Things still needed in the example: - -* AWS network infrastructure on what is created -* Full explanation on why certain resources are created -* Create EC2 instance specific SecurityGroup instead of using the default one from VPC module -* Push logs of default EC2 stuff (docker, ecs agent, etc...) to CloudWatch logs -* Add an example with ALB -* Add an example with NLB -* Add an example with ELB -* Create a Fargate example - -## Usage - -To run this example you need to execute: - -```bash -terraform init -terraform plan -terraform apply -``` - -Note that this example may create resources which can cost money (AWS EC2 instances, for example). Run `terraform destroy` when you don't need these resources. - -## Explanation - -Current version creates an high-available VPC with instances that are attached to ECS. ECS tasks can be run on these instances but they are not exposed to anything. - - -## Requirements - -| Name | Version | -|------|---------| -| [terraform](#requirement\_terraform) | >= 1.0 | -| [aws](#requirement\_aws) | >= 4.6 | - -## Providers - -| Name | Version | -|------|---------| -| [aws](#provider\_aws) | >= 4.6 | - -## Modules - -| Name | Source | Version | -|------|--------|---------| -| [asg](#module\_asg) | terraform-aws-modules/autoscaling/aws | ~> 4.0 | -| [disabled\_ecs](#module\_disabled\_ecs) | ../../ | n/a | -| [ec2\_profile](#module\_ec2\_profile) | ../../modules/ecs-instance-profile | n/a | -| [ecs](#module\_ecs) | ../../ | n/a | -| [hello\_world](#module\_hello\_world) | ./service-hello-world | n/a | -| [vpc](#module\_vpc) | terraform-aws-modules/vpc/aws | ~> 3.0 | - -## Resources - -| Name | Type | -|------|------| -| [aws_ecs_capacity_provider.prov1](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/ecs_capacity_provider) | resource | -| [aws_ami.amazon_linux_ecs](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/ami) | data source | -| [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 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\_id](#output\_cluster\_id) | ID that identifies the cluster | - diff --git a/examples/complete-ecs/main.tf b/examples/complete-ecs/main.tf deleted file mode 100644 index ac32822..0000000 --- a/examples/complete-ecs/main.tf +++ /dev/null @@ -1,133 +0,0 @@ -locals { - name = "complete-ecs" - environment = "dev" - - # This is the convention we use to know what belongs to each other - ec2_resources_name = "${local.name}-${local.environment}" -} - -data "aws_availability_zones" "available" { - state = "available" -} - -module "vpc" { - source = "terraform-aws-modules/vpc/aws" - version = "~> 3.0" - - name = local.name - - cidr = "10.1.0.0/16" - - azs = [data.aws_availability_zones.available.names[0], data.aws_availability_zones.available.names[1]] - private_subnets = ["10.1.1.0/24", "10.1.2.0/24"] - public_subnets = ["10.1.11.0/24", "10.1.12.0/24"] - - enable_nat_gateway = false # false is just faster - - tags = { - Environment = local.environment - Name = local.name - } -} - -#----- ECS -------- -module "ecs" { - source = "../../" - - cluster_name = local.name - - # capacity_providers = [aws_ecs_capacity_provider.prov1.name] - # cluster_default_capacity_provider_strategy = [{ - # capacity_provider = aws_ecs_capacity_provider.prov1.name # "FARGATE_SPOT" - # weight = "1" - # }] - - tags = { - Environment = local.environment - } -} - -module "ec2_profile" { - source = "../../modules/ecs-instance-profile" - - name = local.name - - tags = { - Environment = local.environment - } -} - -resource "aws_ecs_capacity_provider" "prov1" { - name = "prov1" - - auto_scaling_group_provider { - auto_scaling_group_arn = module.asg.autoscaling_group_arn - } - -} - -#----- ECS Services-------- -module "hello_world" { - source = "./service-hello-world" - - cluster_id = module.ecs.cluster_id -} - -#----- ECS Resources-------- - -#For now we only use the AWS ECS optimized ami -data "aws_ami" "amazon_linux_ecs" { - most_recent = true - - owners = ["amazon"] - - filter { - name = "name" - values = ["amzn-ami-*-amazon-ecs-optimized"] - } - - filter { - name = "owner-alias" - values = ["amazon"] - } -} - -module "asg" { - source = "terraform-aws-modules/autoscaling/aws" - version = "~> 4.0" - - name = local.ec2_resources_name - - # Launch configuration - lc_name = local.ec2_resources_name - use_lc = true - create_lc = true - - image_id = data.aws_ami.amazon_linux_ecs.id - instance_type = "t2.micro" - security_groups = [module.vpc.default_security_group_id] - iam_instance_profile_name = module.ec2_profile.iam_instance_profile_id - user_data = templatefile("${path.module}/templates/user-data.sh", { - cluster_name = local.name - }) - - # Auto scaling group - vpc_zone_identifier = module.vpc.private_subnets - health_check_type = "EC2" - min_size = 0 - max_size = 2 - desired_capacity = 0 # we don't need them for the example - wait_for_capacity_timeout = 0 - - # tags = local.tags -} - -################### -# Disabled cluster -################### - -module "disabled_ecs" { - source = "../../" - - create = false -} diff --git a/examples/complete-ecs/templates/user-data.sh b/examples/complete-ecs/templates/user-data.sh deleted file mode 100644 index ec4e61c..0000000 --- a/examples/complete-ecs/templates/user-data.sh +++ /dev/null @@ -1,10 +0,0 @@ -#!/bin/bash - -# ECS config -{ - echo "ECS_CLUSTER=${cluster_name}" -} >> /etc/ecs/ecs.config - -start ecs - -echo "Done" diff --git a/examples/ec2/README.md b/examples/ec2/README.md new file mode 100644 index 0000000..0fc93f5 --- /dev/null +++ b/examples/ec2/README.md @@ -0,0 +1,65 @@ +# ECS Cluster w/ EC2 Autoscaling + +Configuration in this directory creates: + +- ECS cluster using autoscaling group capacity provider +- Autoscaling groups with IAM instance profile to be used by ECS cluster +- Example ECS 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.0 | +| [aws](#requirement\_aws) | >= 4.6 | + +## Providers + +| Name | Version | +|------|---------| +| [aws](#provider\_aws) | >= 4.6 | + +## Modules + +| Name | Source | Version | +|------|--------|---------| +| [autoscaling](#module\_autoscaling) | terraform-aws-modules/autoscaling/aws | ~> 6.5 | +| [autoscaling\_sg](#module\_autoscaling\_sg) | terraform-aws-modules/security-group/aws | ~> 4.0 | +| [ecs](#module\_ecs) | ../.. | n/a | +| [ecs\_disabled](#module\_ecs\_disabled) | ../.. | n/a | +| [hello\_world](#module\_hello\_world) | ./service-hello-world | n/a | +| [vpc](#module\_vpc) | terraform-aws-modules/vpc/aws | ~> 3.0 | + +## Resources + +| Name | Type | +|------|------| +| [aws_cloudwatch_log_group.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudwatch_log_group) | resource | +| [aws_ssm_parameter.ecs_optimised_ami](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/ssm_parameter) | data source | + +## Inputs + +No inputs. + +## Outputs + +| Name | Description | +|------|-------------| +| [capacity\_providers](#output\_capacity\_providers) | Map of capacity providers created and their attributes | +| [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 | + diff --git a/examples/ec2/main.tf b/examples/ec2/main.tf new file mode 100644 index 0000000..b464125 --- /dev/null +++ b/examples/ec2/main.tf @@ -0,0 +1,201 @@ +provider "aws" { + region = local.region +} + +locals { + region = "eu-west-1" + name = "ecs-ex-${replace(basename(path.cwd), "_", "-")}" + + user_data = <<-EOT + #!/bin/bash + cat <<'EOF' >> /etc/ecs/ecs.config + ECS_CLUSTER=${local.name} + ECS_LOGLEVEL=debug + EOF + EOT + + tags = { + Name = local.name + Example = local.name + Repository = "https://github.com/terraform-aws-modules/terraform-aws-ecs" + } +} + +################################################################################ +# ECS Module +################################################################################ + +module "ecs_disabled" { + source = "../.." + + create = false +} + +module "ecs" { + source = "../.." + + cluster_name = local.name + + cluster_configuration = { + execute_command_configuration = { + logging = "OVERRIDE" + log_configuration = { + # You can set a simple string and ECS will create the CloudWatch log group for you + # or you can create the resource yourself as shown here to better manage retetion, tagging, etc. + # Embedding it into the module is not trivial and therefore it is externalized + cloud_watch_log_group_name = aws_cloudwatch_log_group.this.name + } + } + } + + # Capacity provider - autoscaling groups + capacity_providers = { + one = { + auto_scaling_group_arn = module.autoscaling["one"].autoscaling_group_arn + managed_termination_protection = "ENABLED" + + managed_scaling = { + maximum_scaling_step_size = 5 + minimum_scaling_step_size = 1 + status = "ENABLED" + target_capacity = 60 + } + + default_capacity_provider_strategy = { + weight = 60 + base = 20 + } + } + two = { + auto_scaling_group_arn = module.autoscaling["two"].autoscaling_group_arn + managed_termination_protection = "ENABLED" + + managed_scaling = { + maximum_scaling_step_size = 15 + minimum_scaling_step_size = 5 + status = "ENABLED" + target_capacity = 90 + } + + default_capacity_provider_strategy = { + weight = 40 + } + } + } + + tags = local.tags +} + +module "hello_world" { + source = "./service-hello-world" + + cluster_id = module.ecs.cluster_id +} + +################################################################################ +# Supporting Resources +################################################################################ + +# https://docs.aws.amazon.com/AmazonECS/latest/developerguide/ecs-optimized_AMI.html#ecs-optimized-ami-linux +data "aws_ssm_parameter" "ecs_optimised_ami" { + name = "/aws/service/ecs/optimized-ami/amazon-linux-2/recommended" +} + +module "autoscaling" { + source = "terraform-aws-modules/autoscaling/aws" + version = "~> 6.5" + + for_each = { + one = { + instance_type = "t3.micro" + } + two = { + instance_type = "t3.small" + } + } + + name = "${local.name}-${each.key}" + + image_id = jsondecode(data.aws_ssm_parameter.ecs_optimised_ami.value)["image_id"] + instance_type = each.value.instance_type + ebs_optimized = true + enable_monitoring = true + + security_groups = [module.autoscaling_sg.security_group_id] + user_data = base64encode(local.user_data) + ignore_desired_capacity_changes = true + + create_iam_instance_profile = true + iam_role_name = local.name + iam_role_description = "ECS role for ${local.name}" + iam_role_policies = { + AmazonEC2ContainerServiceforEC2Role = "arn:aws:iam::aws:policy/service-role/AmazonEC2ContainerServiceforEC2Role" + AmazonSSMManagedInstanceCore = "arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore" + } + + vpc_zone_identifier = module.vpc.private_subnets + health_check_type = "EC2" + min_size = 0 + max_size = 2 + desired_capacity = 1 + + # https://github.com/hashicorp/terraform-provider-aws/issues/12582 + autoscaling_group_tags = { + AmazonECSManaged = true + } + + # Required for managed_termination_protection = "ENABLED" + protect_from_scale_in = true + + tags = local.tags +} + +module "autoscaling_sg" { + source = "terraform-aws-modules/security-group/aws" + version = "~> 4.0" + + name = local.name + description = "Autoscaling group security group" + vpc_id = module.vpc.vpc_id + + ingress_cidr_blocks = ["0.0.0.0/0"] + ingress_rules = ["https-443-tcp"] + + egress_rules = ["all-all"] + + tags = local.tags +} + +module "vpc" { + source = "terraform-aws-modules/vpc/aws" + version = "~> 3.0" + + name = local.name + cidr = "10.99.0.0/18" + + azs = ["${local.region}a", "${local.region}b", "${local.region}c"] + public_subnets = ["10.99.0.0/24", "10.99.1.0/24", "10.99.2.0/24"] + private_subnets = ["10.99.3.0/24", "10.99.4.0/24", "10.99.5.0/24"] + + enable_nat_gateway = true + single_nat_gateway = true + enable_dns_hostnames = true + map_public_ip_on_launch = false + + # Manage so we can name + manage_default_network_acl = true + default_network_acl_tags = { Name = "${local.name}-default" } + manage_default_route_table = true + default_route_table_tags = { Name = "${local.name}-default" } + manage_default_security_group = true + default_security_group_tags = { Name = "${local.name}-default" } + + tags = local.tags +} + +resource "aws_cloudwatch_log_group" "this" { + name = "/aws/ecs/${local.name}" + retention_in_days = 7 + + tags = local.tags +} diff --git a/examples/complete-ecs/outputs.tf b/examples/ec2/outputs.tf similarity index 74% rename from examples/complete-ecs/outputs.tf rename to examples/ec2/outputs.tf index 51e778c..0483905 100644 --- a/examples/complete-ecs/outputs.tf +++ b/examples/ec2/outputs.tf @@ -13,18 +13,14 @@ output "cluster_id" { } ################################################################################ -# CloudWatch Log Group +# Cluster Capacity Providers ################################################################################ -output "cloudwatch_log_group_name" { - description = "Name of cloudwatch log group created" - value = module.ecs.cloudwatch_log_group_name +output "cluster_capacity_providers" { + description = "Map of cluster capacity providers attributes" + value = module.ecs.cluster_capacity_providers } -output "cloudwatch_log_group_arn" { - description = "Arn of cloudwatch log group created" - value = module.ecs.cloudwatch_log_group_arn -} ################################################################################ # Capacity Provider ################################################################################ diff --git a/examples/complete-ecs/service-hello-world/main.tf b/examples/ec2/service-hello-world/main.tf similarity index 60% rename from examples/complete-ecs/service-hello-world/main.tf rename to examples/ec2/service-hello-world/main.tf index 91a2953..caea1dd 100644 --- a/examples/complete-ecs/service-hello-world/main.tf +++ b/examples/ec2/service-hello-world/main.tf @@ -1,9 +1,9 @@ -resource "aws_cloudwatch_log_group" "hello_world" { - name = "hello_world" +resource "aws_cloudwatch_log_group" "this" { + name_prefix = "hello_world-" retention_in_days = 1 } -resource "aws_ecs_task_definition" "hello_world" { +resource "aws_ecs_task_definition" "this" { family = "hello_world" container_definitions = < +- ECS cluster using Fargate (on-demand and spot) capacity provider ## Usage @@ -36,13 +36,12 @@ Note that this example may create resources which will incur monetary charges on |------|--------|---------| | [ecs](#module\_ecs) | ../.. | n/a | | [ecs\_disabled](#module\_ecs\_disabled) | ../.. | n/a | -| [vpc](#module\_vpc) | terraform-aws-modules/vpc/aws | ~> 3.0 | ## Resources | Name | Type | |------|------| -| [aws_kms_key.example](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/kms_key) | resource | +| [aws_cloudwatch_log_group.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudwatch_log_group) | resource | ## Inputs @@ -53,8 +52,7 @@ No inputs. | Name | Description | |------|-------------| | [capacity\_providers](#output\_capacity\_providers) | Map of 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 | diff --git a/examples/fargate/main.tf b/examples/fargate/main.tf index 64200bd..572eb27 100644 --- a/examples/fargate/main.tf +++ b/examples/fargate/main.tf @@ -3,7 +3,7 @@ provider "aws" { } locals { - region = "us-east-1" + region = "eu-west-1" name = "ecs-ex-${replace(basename(path.cwd), "_", "-")}" tags = { @@ -27,26 +27,32 @@ module "ecs" { source = "../.." cluster_name = local.name + cluster_configuration = { execute_command_configuration = { - kms_key_id = aws_kms_key.example.arn - logging = "OVERRIDE" + logging = "OVERRIDE" + log_configuration = { + # You can set a simple string and ECS will create the CloudWatch log group for you + # or you can create the resource yourself as shown here to better manage retetion, tagging, etc. + # Embedding it into the module is not trivial and therefore it is externalized + cloud_watch_log_group_name = aws_cloudwatch_log_group.this.name + } } } # Capacity provider - cluster_capacity_providers = ["FARGATE", "FARGATE_SPOT"] - cluster_default_capacity_provider_strategy = [ - { - - capacity_provider = "FARGATE" - weight = 50 - }, - { - capacity_provider = "FARGATE_SPOT" - weight = 50 + cluster_capacity_providers = { + "FARGATE" = { + default_capacity_provider_strategy = { + weight = 50 + } + } + "FARGATE_SPOT" = { + default_capacity_provider_strategy = { + weight = 50 + } } - ] + } tags = local.tags } @@ -55,29 +61,9 @@ module "ecs" { # Supporting Resources ################################################################################ -module "vpc" { - source = "terraform-aws-modules/vpc/aws" - version = "~> 3.0" - - create_vpc = false - - name = local.name - cidr = "10.99.0.0/18" - - azs = ["${local.region}a", "${local.region}b", "${local.region}c"] - public_subnets = ["10.99.0.0/24", "10.99.1.0/24", "10.99.2.0/24"] - private_subnets = ["10.99.3.0/24", "10.99.4.0/24", "10.99.5.0/24"] - - enable_nat_gateway = true - single_nat_gateway = true - map_public_ip_on_launch = false - - tags = local.tags -} - -resource "aws_kms_key" "example" { - description = local.name - deletion_window_in_days = 7 +resource "aws_cloudwatch_log_group" "this" { + name = "/aws/ecs/${local.name}" + retention_in_days = 7 tags = local.tags } diff --git a/examples/fargate/outputs.tf b/examples/fargate/outputs.tf index 51e778c..0483905 100644 --- a/examples/fargate/outputs.tf +++ b/examples/fargate/outputs.tf @@ -13,18 +13,14 @@ output "cluster_id" { } ################################################################################ -# CloudWatch Log Group +# Cluster Capacity Providers ################################################################################ -output "cloudwatch_log_group_name" { - description = "Name of cloudwatch log group created" - value = module.ecs.cloudwatch_log_group_name +output "cluster_capacity_providers" { + description = "Map of cluster capacity providers attributes" + value = module.ecs.cluster_capacity_providers } -output "cloudwatch_log_group_arn" { - description = "Arn of cloudwatch log group created" - value = module.ecs.cloudwatch_log_group_arn -} ################################################################################ # Capacity Provider ################################################################################ diff --git a/main.tf b/main.tf index ba90404..9210539 100644 --- a/main.tf +++ b/main.tf @@ -2,39 +2,24 @@ # Cluster ################################################################################ -locals { - # Used to default to logs enabled and sent to cloudwatch group created by module - cluster_configuration = merge( - { - execute_command_configuration = { - log_configuration = { - cloud_watch_log_group_name = var.create_cloudwatch_log_group ? aws_cloudwatch_log_group.this[0].name : null - } - } - }, - var.cluster_configuration, - ) -} - resource "aws_ecs_cluster" "this" { count = var.create ? 1 : 0 name = var.cluster_name - dynamic "configuration" { - for_each = [local.cluster_configuration] + for_each = try([var.cluster_configuration], []) content { dynamic "execute_command_configuration" { - for_each = [configuration.value.execute_command_configuration] + for_each = try([configuration.value.execute_command_configuration], []) content { kms_key_id = try(execute_command_configuration.value.kms_key_id, null) logging = try(execute_command_configuration.value.logging, "DEFAULT") dynamic "log_configuration" { - for_each = [execute_command_configuration.value.log_configuration] + for_each = try([execute_command_configuration.value.log_configuration], []) content { cloud_watch_encryption_enabled = try(log_configuration.value.cloud_watch_encryption_enabled, null) @@ -62,36 +47,37 @@ resource "aws_ecs_cluster" "this" { } ################################################################################ -# CloudWatch Log Group +# Cluster Capacity Providers ################################################################################ -resource "aws_cloudwatch_log_group" "this" { - count = var.create && var.create_cloudwatch_log_group ? 1 : 0 - - name = coalesce(var.cloudwatch_log_group_name, "/aws/ecs/${var.cluster_name}") - retention_in_days = var.cloudwatch_log_group_retention_in_days - kms_key_id = var.cloudwatch_log_group_kms_key_id - - tags = var.tags +locals { + # We are merging these together so that we can reference the ECS capacity provider + # (ec2 autoscaling) created in this module below. Fargate is easy since its just + # a static string, but the ECs cappacity provider needs to be self-referenced from + # within this module. Therefore the input schema of `var.cluster_capacity_providers` + # is customized to allow for both routes + cluster_capacity_providers = merge( + var.cluster_capacity_providers, + { for k, v in var.capacity_providers : k => merge(aws_ecs_capacity_provider.this[k], v) } + ) } -################################################################################ -# Cluster Capacity Providers - Fargate -################################################################################ - resource "aws_ecs_cluster_capacity_providers" "this" { count = var.create ? 1 : 0 - cluster_name = aws_ecs_cluster.this[0].name - capacity_providers = var.cluster_capacity_providers + cluster_name = aws_ecs_cluster.this[0].name + capacity_providers = distinct(concat( + [for k, v in var.cluster_capacity_providers : try(v.name, k)], + [for k, v in var.capacity_providers : try(v.name, k)] + )) dynamic "default_capacity_provider_strategy" { - for_each = var.cluster_default_capacity_provider_strategy + for_each = local.cluster_capacity_providers content { - capacity_provider = default_capacity_provider_strategy.value.capacity_provider - base = try(default_capacity_provider_strategy.value.base, null) - weight = try(default_capacity_provider_strategy.value.weight, null) + capacity_provider = try(default_capacity_provider_strategy.value.name, default_capacity_provider_strategy.key) + base = try(default_capacity_provider_strategy.value.default_capacity_provider_strategy.base, null) + weight = try(default_capacity_provider_strategy.value.default_capacity_provider_strategy.weight, null) } } } diff --git a/modules/ecs-instance-profile/README.md b/modules/ecs-instance-profile/README.md deleted file mode 100644 index ee2f957..0000000 --- a/modules/ecs-instance-profile/README.md +++ /dev/null @@ -1,53 +0,0 @@ -# ECS instance policy - -For an EC2 instance to connect itself to ECS it needs rights to do so. - -* [Why do we need ECS instance policies?](http://docs.aws.amazon.com/AmazonECS/latest/developerguide/instance_IAM_role.html) -* [ECS roles explained](http://docs.aws.amazon.com/AmazonECS/latest/developerguide/ecs_managed_policies.html) -* [More ECS policy examples explained](http://docs.aws.amazon.com/AmazonECS/latest/developerguide/IAMPolicyExamples.html) - - -## Requirements - -| Name | Version | -|------|---------| -| [terraform](#requirement\_terraform) | >= 0.13.1 | -| [aws](#requirement\_aws) | >= 2.48 | - -## Providers - -| Name | Version | -|------|---------| -| [aws](#provider\_aws) | >= 2.48 | - -## Modules - -No modules. - -## Resources - -| Name | Type | -|------|------| -| [aws_iam_instance_profile.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_instance_profile) | resource | -| [aws_iam_role.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role) | resource | -| [aws_iam_role_policy_attachment.amazon_ssm_managed_instance_core](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource | -| [aws_iam_role_policy_attachment.ecs_ec2_cloudwatch_role](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource | -| [aws_iam_role_policy_attachment.ecs_ec2_role](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource | -| [aws_partition.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/partition) | data source | - -## Inputs - -| Name | Description | Type | Default | Required | -|------|-------------|------|---------|:--------:| -| [include\_ssm](#input\_include\_ssm) | Whether to include policies needed for AmazonSSM | `bool` | `false` | no | -| [name](#input\_name) | Name to be used on all the resources as identifier | `string` | n/a | yes | -| [tags](#input\_tags) | A map of tags to add to instance profile role | `map(string)` | `{}` | no | - -## Outputs - -| Name | Description | -|------|-------------| -| [iam\_instance\_profile\_arn](#output\_iam\_instance\_profile\_arn) | ARN of the IAM instance profile | -| [iam\_instance\_profile\_id](#output\_iam\_instance\_profile\_id) | ID of the IAM instance profile | -| [iam\_role\_id](#output\_iam\_role\_id) | ID of the IAM role | - diff --git a/modules/ecs-instance-profile/main.tf b/modules/ecs-instance-profile/main.tf deleted file mode 100644 index a58d9cb..0000000 --- a/modules/ecs-instance-profile/main.tf +++ /dev/null @@ -1,47 +0,0 @@ -data "aws_partition" "current" {} - -resource "aws_iam_role" "this" { - name = "${var.name}_ecs_instance_role" - path = "/ecs/" - - tags = var.tags - - assume_role_policy = < +## Requirements + +| Name | Version | +|------|---------| +| [terraform](#requirement\_terraform) | >= 1.0 | +| [aws](#requirement\_aws) | >= 4.6 | + +## Providers + +No providers. + +## Modules + +No modules. + +## Resources + +No resources. + +## Inputs + +No inputs. + +## Outputs + +No outputs. + diff --git a/modules/service/main.tf b/modules/service/main.tf new file mode 100644 index 0000000..066066c --- /dev/null +++ b/modules/service/main.tf @@ -0,0 +1 @@ +locals {} diff --git a/modules/service/outputs.tf b/modules/service/outputs.tf new file mode 100644 index 0000000..e69de29 diff --git a/modules/service/variables.tf b/modules/service/variables.tf new file mode 100644 index 0000000..e69de29 diff --git a/modules/ecs-instance-profile/versions.tf b/modules/service/versions.tf similarity index 60% rename from modules/ecs-instance-profile/versions.tf rename to modules/service/versions.tf index 5e5c16f..35402be 100644 --- a/modules/ecs-instance-profile/versions.tf +++ b/modules/service/versions.tf @@ -1,10 +1,10 @@ terraform { - required_version = ">= 0.13.1" + required_version = ">= 1.0" required_providers { aws = { source = "hashicorp/aws" - version = ">= 2.48" + version = ">= 4.6" } } } diff --git a/outputs.tf b/outputs.tf index c762494..6fa327e 100644 --- a/outputs.tf +++ b/outputs.tf @@ -13,17 +13,12 @@ output "cluster_id" { } ################################################################################ -# CloudWatch Log Group +# Cluster Capacity Providers ################################################################################ -output "cloudwatch_log_group_name" { - description = "Name of cloudwatch log group created" - value = try(aws_cloudwatch_log_group.this[0].name, null) -} - -output "cloudwatch_log_group_arn" { - description = "Arn of cloudwatch log group created" - value = try(aws_cloudwatch_log_group.this[0].arn, null) +output "cluster_capacity_providers" { + description = "Map of cluster capacity providers attributes" + value = aws_ecs_cluster_capacity_providers.this } ################################################################################ diff --git a/variables.tf b/variables.tf index 50e2f46..a0c5086 100644 --- a/variables.tf +++ b/variables.tf @@ -35,48 +35,14 @@ variable "cluster_settings" { } } -################################################################################ -# CloudWatch Log Group -################################################################################ - -variable "create_cloudwatch_log_group" { - description = "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" - type = bool - default = true -} - -variable "cloudwatch_log_group_name" { - description = "Name of the cloudwatch log group created. If not provided, defaults to `/aws/ecs/`" - type = string - default = null -} - -variable "cloudwatch_log_group_retention_in_days" { - description = "Number of days to retain log events. Default retention - 30 days" - type = number - default = 30 -} - -variable "cloudwatch_log_group_kms_key_id" { - description = "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)" - type = string - default = null -} - ################################################################################ # Cluster Capacity Providers ################################################################################ variable "cluster_capacity_providers" { description = "The capacity providers to use for the cluster" - type = list(string) - default = [] -} - -variable "cluster_default_capacity_provider_strategy" { - description = "The default capacity provider strategy to use for the cluster" - type = list(map(string)) - default = [] + type = any + default = {} } ################################################################################ From c2fc0e3f7f6e01f96a72957236b869e70a269a7a Mon Sep 17 00:00:00 2001 From: Bryant Biggs Date: Fri, 3 Jun 2022 16:14:40 -0400 Subject: [PATCH 3/7] refactor: Update variable names to help make sense, update main README --- README.md | 176 +++++++++++++++++++++++++++++++++--- examples/ec2/README.md | 2 +- examples/ec2/main.tf | 2 +- examples/ec2/outputs.tf | 4 +- examples/fargate/README.md | 2 +- examples/fargate/main.tf | 3 +- examples/fargate/outputs.tf | 4 +- main.tf | 15 ++- outputs.tf | 4 +- variables.tf | 14 +-- 10 files changed, 184 insertions(+), 42 deletions(-) diff --git a/README.md b/README.md index 89e2de5..b150a50 100644 --- a/README.md +++ b/README.md @@ -1,31 +1,177 @@ -# AWS Elastic Container Service (ECS) Terraform module +# AWS ECS Terraform module -Terraform module which creates ECS resources on AWS. +Terraform module which creates ECS (Elastic Container Service) resources on AWS. ## Available Features -- ECS cluster w/ support for EC2 AutoScaling Group and/or Fargate provisioners +- ECS cluster +- Fargate capacity providers +- EC2 AutoScaling Group capacity providers ## Usage +### Fargate Capacity Providers + +```hcl +module "ecs" { + source = "terraform-aws-modules/ecs/aws" + + name = "ecs-fargate" + + cluster_configuration = { + execute_command_configuration = { + logging = "OVERRIDE" + log_configuration = { + cloud_watch_log_group_name = "/aws/ecs/aws-ec2" + } + } + } + + fargate_capacity_providers = { + "FARGATE" = { + default_capacity_provider_strategy = { + weight = 50 + } + } + "FARGATE_SPOT" = { + default_capacity_provider_strategy = { + weight = 50 + } + } + } + + tags = { + Environment = "Development" + Project = "EcsEc2" + } +} +``` + +### EC2 Autoscaling Capacity Providers + +```hcl +module "ecs" { + source = "terraform-aws-modules/ecs/aws" + + name = "ecs-ec2" + + cluster_configuration = { + execute_command_configuration = { + logging = "OVERRIDE" + log_configuration = { + cloud_watch_log_group_name = "/aws/ecs/aws-ec2" + } + } + } + + autoscaling_capacity_providers = { + one = { + auto_scaling_group_arn = "arn:aws:autoscaling:eu-west-1:012345678901:autoScalingGroup:08419a61:autoScalingGroupName/ecs-ec2-one-20220603194933774300000011" + managed_termination_protection = "ENABLED" + + managed_scaling = { + maximum_scaling_step_size = 5 + minimum_scaling_step_size = 1 + status = "ENABLED" + target_capacity = 60 + } + + default_capacity_provider_strategy = { + weight = 60 + base = 20 + } + } + two = { + auto_scaling_group_arn = "arn:aws:autoscaling:eu-west-1:012345678901:autoScalingGroup:08419a61:autoScalingGroupName/ecs-ec2-two-20220603194933774300000022" + managed_termination_protection = "ENABLED" + + managed_scaling = { + maximum_scaling_step_size = 15 + minimum_scaling_step_size = 5 + status = "ENABLED" + target_capacity = 90 + } + + default_capacity_provider_strategy = { + weight = 40 + } + } + } + + tags = { + Environment = "Development" + Project = "EcsEc2" + } +} +``` + +### Fargate & EC2 Autoscaling Capacity Providers + ```hcl module "ecs" { source = "terraform-aws-modules/ecs/aws" - name = "my-ecs" + name = "ecs-mixed" - container_insights = true + cluster_configuration = { + execute_command_configuration = { + logging = "OVERRIDE" + log_configuration = { + cloud_watch_log_group_name = "/aws/ecs/aws-ec2" + } + } + } - capacity_providers = ["FARGATE", "FARGATE_SPOT"] + fargate_capacity_providers = { + "FARGATE" = { + default_capacity_provider_strategy = { + weight = 50 + } + } + "FARGATE_SPOT" = { + default_capacity_provider_strategy = { + weight = 50 + } + } + } - default_capacity_provider_strategy = [ - { - capacity_provider = "FARGATE_SPOT" + autoscaling_capacity_providers = { + one = { + auto_scaling_group_arn = "arn:aws:autoscaling:eu-west-1:012345678901:autoScalingGroup:08419a61:autoScalingGroupName/ecs-ec2-one-20220603194933774300000011" + managed_termination_protection = "ENABLED" + + managed_scaling = { + maximum_scaling_step_size = 5 + minimum_scaling_step_size = 1 + status = "ENABLED" + target_capacity = 60 + } + + default_capacity_provider_strategy = { + weight = 60 + base = 20 + } } - ] + two = { + auto_scaling_group_arn = "arn:aws:autoscaling:eu-west-1:012345678901:autoScalingGroup:08419a61:autoScalingGroupName/ecs-ec2-two-20220603194933774300000022" + managed_termination_protection = "ENABLED" + + managed_scaling = { + maximum_scaling_step_size = 15 + minimum_scaling_step_size = 5 + status = "ENABLED" + target_capacity = 90 + } + + default_capacity_provider_strategy = { + weight = 40 + } + } + } tags = { Environment = "Development" + Project = "EcsEc2" } } ``` @@ -47,8 +193,8 @@ module "ecs" { ## Examples -- [ECS Cluster w/ EC2 Autoscaling](https://github.com/terraform-aws-modules/terraform-aws-ecs/tree/master/examples/ec2) -- [ECS Clusters w/ Fargate](https://github.com/terraform-aws-modules/terraform-aws-ecs/tree/master/examples/fargate) +- [ECS Cluster w/ EC2 Autoscaling Capacity Provider](https://github.com/terraform-aws-modules/terraform-aws-ecs/tree/master/examples/ec2) +- [ECS Cluster w/ Fargate Capacity Provider](https://github.com/terraform-aws-modules/terraform-aws-ecs/tree/master/examples/fargate) ## Requirements @@ -80,19 +226,19 @@ No modules. | Name | Description | Type | Default | Required | |------|-------------|------|---------|:--------:| -| [capacity\_providers](#input\_capacity\_providers) | Map of capacity provider definitons to create | `any` | `{}` | no | -| [cluster\_capacity\_providers](#input\_cluster\_capacity\_providers) | The capacity providers to use for the cluster | `any` | `{}` | no | +| [autoscaling\_capacity\_providers](#input\_autoscaling\_capacity\_providers) | Map of autoscaling capacity provider definitons to create for the cluster | `any` | `{}` | no | | [cluster\_configuration](#input\_cluster\_configuration) | The execute command configuration for the cluster | `any` | `{}` | no | | [cluster\_name](#input\_cluster\_name) | Name of the cluster (up to 255 letters, numbers, hyphens, and underscores) | `string` | `""` | no | | [cluster\_settings](#input\_cluster\_settings) | Configuration block(s) with cluster settings. For example, this can be used to enable CloudWatch Container Insights for a cluster | `map(string)` |
{
"name": "containerInsights",
"value": "enabled"
}
| no | | [create](#input\_create) | Determines whether resources will be created (affects all resources) | `bool` | `true` | no | +| [fargate\_capacity\_providers](#input\_fargate\_capacity\_providers) | Map of Fargate capacity provider definitions to use for the cluster | `any` | `{}` | no | | [tags](#input\_tags) | A map of tags to add to all resources | `map(string)` | `{}` | no | ## Outputs | Name | Description | |------|-------------| -| [capacity\_providers](#output\_capacity\_providers) | Map of capacity providers created and their attributes | +| [autoscaling\_capacity\_providers](#output\_autoscaling\_capacity\_providers) | Map of autoscaling capacity providers created and their attributes | | [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 | diff --git a/examples/ec2/README.md b/examples/ec2/README.md index 0fc93f5..5df64b0 100644 --- a/examples/ec2/README.md +++ b/examples/ec2/README.md @@ -58,7 +58,7 @@ No inputs. | Name | Description | |------|-------------| -| [capacity\_providers](#output\_capacity\_providers) | Map of capacity providers created and their attributes | +| [autoscaling\_capacity\_providers](#output\_autoscaling\_capacity\_providers) | Map of capacity providers created and their attributes | | [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 | diff --git a/examples/ec2/main.tf b/examples/ec2/main.tf index b464125..7c37ad2 100644 --- a/examples/ec2/main.tf +++ b/examples/ec2/main.tf @@ -49,7 +49,7 @@ module "ecs" { } # Capacity provider - autoscaling groups - capacity_providers = { + autoscaling_capacity_providers = { one = { auto_scaling_group_arn = module.autoscaling["one"].autoscaling_group_arn managed_termination_protection = "ENABLED" diff --git a/examples/ec2/outputs.tf b/examples/ec2/outputs.tf index 0483905..3190f74 100644 --- a/examples/ec2/outputs.tf +++ b/examples/ec2/outputs.tf @@ -25,7 +25,7 @@ output "cluster_capacity_providers" { # Capacity Provider ################################################################################ -output "capacity_providers" { +output "autoscaling_capacity_providers" { description = "Map of capacity providers created and their attributes" - value = module.ecs.capacity_providers + value = module.ecs.autoscaling_capacity_providers } diff --git a/examples/fargate/README.md b/examples/fargate/README.md index 333e3c4..8a10df7 100644 --- a/examples/fargate/README.md +++ b/examples/fargate/README.md @@ -51,7 +51,7 @@ No inputs. | Name | Description | |------|-------------| -| [capacity\_providers](#output\_capacity\_providers) | Map of capacity providers created and their attributes | +| [autoscaling\_capacity\_providers](#output\_autoscaling\_capacity\_providers) | Map of capacity providers created and their attributes | | [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 | diff --git a/examples/fargate/main.tf b/examples/fargate/main.tf index 572eb27..42d8619 100644 --- a/examples/fargate/main.tf +++ b/examples/fargate/main.tf @@ -41,10 +41,11 @@ module "ecs" { } # Capacity provider - cluster_capacity_providers = { + fargate_capacity_providers = { "FARGATE" = { default_capacity_provider_strategy = { weight = 50 + base = 20 } } "FARGATE_SPOT" = { diff --git a/examples/fargate/outputs.tf b/examples/fargate/outputs.tf index 0483905..3190f74 100644 --- a/examples/fargate/outputs.tf +++ b/examples/fargate/outputs.tf @@ -25,7 +25,7 @@ output "cluster_capacity_providers" { # Capacity Provider ################################################################################ -output "capacity_providers" { +output "autoscaling_capacity_providers" { description = "Map of capacity providers created and their attributes" - value = module.ecs.capacity_providers + value = module.ecs.autoscaling_capacity_providers } diff --git a/main.tf b/main.tf index 9210539..ed641e1 100644 --- a/main.tf +++ b/main.tf @@ -53,12 +53,11 @@ resource "aws_ecs_cluster" "this" { locals { # We are merging these together so that we can reference the ECS capacity provider # (ec2 autoscaling) created in this module below. Fargate is easy since its just - # a static string, but the ECs cappacity provider needs to be self-referenced from - # within this module. Therefore the input schema of `var.cluster_capacity_providers` - # is customized to allow for both routes + # static values, but the autoscaling cappacity provider needs to be self-referenced from + # within this module cluster_capacity_providers = merge( - var.cluster_capacity_providers, - { for k, v in var.capacity_providers : k => merge(aws_ecs_capacity_provider.this[k], v) } + var.fargate_capacity_providers, + { for k, v in var.autoscaling_capacity_providers : k => merge(aws_ecs_capacity_provider.this[k], v) } ) } @@ -67,8 +66,8 @@ resource "aws_ecs_cluster_capacity_providers" "this" { cluster_name = aws_ecs_cluster.this[0].name capacity_providers = distinct(concat( - [for k, v in var.cluster_capacity_providers : try(v.name, k)], - [for k, v in var.capacity_providers : try(v.name, k)] + [for k, v in var.fargate_capacity_providers : try(v.name, k)], + [for k, v in var.autoscaling_capacity_providers : try(v.name, k)] )) dynamic "default_capacity_provider_strategy" { @@ -87,7 +86,7 @@ resource "aws_ecs_cluster_capacity_providers" "this" { ################################################################################ resource "aws_ecs_capacity_provider" "this" { - for_each = { for k, v in var.capacity_providers : k => v if var.create } + for_each = { for k, v in var.autoscaling_capacity_providers : k => v if var.create } name = try(each.value.name, each.key) diff --git a/outputs.tf b/outputs.tf index 6fa327e..dbedbdf 100644 --- a/outputs.tf +++ b/outputs.tf @@ -25,7 +25,7 @@ output "cluster_capacity_providers" { # Capacity Provider - Autoscaling Group(s) ################################################################################ -output "capacity_providers" { - description = "Map of capacity providers created and their attributes" +output "autoscaling_capacity_providers" { + description = "Map of autoscaling capacity providers created and their attributes" value = aws_ecs_capacity_provider.this } diff --git a/variables.tf b/variables.tf index a0c5086..dd66323 100644 --- a/variables.tf +++ b/variables.tf @@ -36,21 +36,17 @@ variable "cluster_settings" { } ################################################################################ -# Cluster Capacity Providers +# Capacity Providers ################################################################################ -variable "cluster_capacity_providers" { - description = "The capacity providers to use for the cluster" +variable "fargate_capacity_providers" { + description = "Map of Fargate capacity provider definitions to use for the cluster" type = any default = {} } -################################################################################ -# Capacity Provider -################################################################################ - -variable "capacity_providers" { - description = "Map of capacity provider definitons to create" +variable "autoscaling_capacity_providers" { + description = "Map of autoscaling capacity provider definitons to create for the cluster" type = any default = {} } From 1525603d47e7639e2e524a5955b85862cbd0f4fb Mon Sep 17 00:00:00 2001 From: Bryant Biggs Date: Fri, 3 Jun 2022 16:47:15 -0400 Subject: [PATCH 4/7] feat: Add upgrade guide details --- UPGRADE-4.0.md | 407 ++++++++++++++++++++++- examples/ec2/service-hello-world/main.tf | 2 +- main.tf | 9 +- 3 files changed, 403 insertions(+), 15 deletions(-) diff --git a/UPGRADE-4.0.md b/UPGRADE-4.0.md index 81bbfc6..b07d64e 100644 --- a/UPGRADE-4.0.md +++ b/UPGRADE-4.0.md @@ -4,47 +4,434 @@ Please consult the `examples` directory for reference example configurations. If ## List of backwards incompatible changes -- Minimum supported version of Terraform AWS provider updated to v4.6 to support latest resources +- Minimum supported version of Terraform AWS provider updated to v4.6 to support the latest resources utilized - Minimum supported version of Terraform updated to v1.0 +- `ecs-instance-profile` sub-module has been removed; this functionality is available through the [`terraform-aws-modules/terraform-aws-autoscaling`](https://github.com/terraform-aws-modules/terraform-aws-autoscaling) module starting with version [v6.5.0](https://github.com/terraform-aws-modules/terraform-aws-autoscaling/pull/194). Please see the [`examples/ec2`](https://github.com/terraform-aws-modules/terraform-aws-ecs/tree/master/examples/ec2) example for a demonstration on how to use and integrate with the `terraform-aws-autoscaling` module. +- The `container_insights` and `capacity_providers` variables have been replaced by new variables - see below for more details ## Additional changes ### Added -- +- Support for `aws_ecs_capacity_provider` has been added to the module ### Modified -- +- The `container_insights` variable has been replaced with the `cluster_settings` variable which allows users to enable/disable container insights and also allows for not specifying at all for regions where container insights is currently not supported. +- The `capacity_providers` variable has been replaced with `fargate_capacity_providers`and `autoscaling_capacity_providers`. This allows users to specify either Fargate based capacity providers, EC2 AutoScaling Group capacity providers, or both. +- Previously `capacity_providers` and `default_capacity_provider_strategy` usage looked like: +```hcl + capacity_providers = ["FARGATE", "FARGATE_SPOT"] + + default_capacity_provider_strategy = [{ + capacity_provider = "FARGATE" + weight = 50 + base = 20 + }, { + capacity_provider = "FARGATE_SPOT" + weight = 50 + }] +``` +Where the current equivalent now looks like: +```hcl + fargate_capacity_providers = { + "FARGATE" = { + default_capacity_provider_strategy = { + weight = 50 + base = 20 + } + } + "FARGATE_SPOT" = { + default_capacity_provider_strategy = { + weight = 50 + } + } + } +``` +- Previously `capacity_providers` accepted the name of an AutoScaling Group created externally; this is now replaced by the usage of `autoscaling_capacity_providers` which incorporates the usage of the newly added support for `aws_ecs_capacity_provider` ### Removed -- +- `ecs-instance-profile` sub-module has been removed; this functionality is available through the [`terraform-aws-modules/terraform-aws-autoscaling`](https://github.com/terraform-aws-modules/terraform-aws-autoscaling) module starting with version [v6.5.0](https://github.com/terraform-aws-modules/terraform-aws-autoscaling/pull/194). Please see the [`examples/ec2`](https://github.com/terraform-aws-modules/terraform-aws-ecs/tree/master/examples/ec2) example for a demonstration on how to use and integrate with the `terraform-aws-autoscaling` module. ### Variable and output changes 1. Removed variables: - - + - `default_capacity_provider_strategy` is now incorporated into the `fargate_capacity_providers` and `autoscaling_capacity_providers` variables. 2. Renamed variables: - - + - `create_ecs` -> `create` + - `name` -> `cluster_name` 3. Added variables: - - + - `cluster_configuration` has been added under a dynamic block with all current attributes supported 4. Removed outputs: - - + - `ecs_cluster_name` 5. Renamed outputs: - - + - `ecs_cluster_id` -> `cluster_id` + - `ecs_cluster_arn` -> `cluster_arn` 6. Added outputs: - - + - `cluster_capacity_providers` + - `autoscaling_capacity_providers` ## Upgrade Migrations + +### Before v3.x Example + +```hcl +provider "aws" { + region = local.region +} + +locals { + region = "eu-west-1" + name = "ecs-ex-${replace(basename(path.cwd), "_", "-")}" + + user_data = <<-EOT + #!/bin/bash + cat <<'EOF' >> /etc/ecs/ecs.config + ECS_CLUSTER=${local.name} + ECS_LOGLEVEL=debug + EOF + EOT + + tags = { + Name = local.name + Example = local.name + Repository = "https://github.com/terraform-aws-modules/terraform-aws-ecs" + } +} + +################################################################################ +# ECS Module +################################################################################ + +module "ecs" { + source = "../../" + + name = local.name + container_insights = true + + capacity_providers = ["FARGATE", "FARGATE_SPOT", aws_ecs_capacity_provider.prov1.name] + + default_capacity_provider_strategy = [{ + capacity_provider = aws_ecs_capacity_provider.prov1.name # "FARGATE_SPOT" + weight = "1" + }] + + tags = local.tags +} + +module "ec2_profile" { + source = "../../modules/ecs-instance-profile" + + name = local.name + + tags = local.tags +} + +resource "aws_ecs_capacity_provider" "prov1" { + name = "prov1" + + auto_scaling_group_provider { + auto_scaling_group_arn = module.autoscaling.autoscaling_group_arn + } +} + +################################################################################ +# Supporting Resources +################################################################################ + +# https://docs.aws.amazon.com/AmazonECS/latest/developerguide/ecs-optimized_AMI.html#ecs-optimized-ami-linux +data "aws_ssm_parameter" "ecs_optimised_ami" { + name = "/aws/service/ecs/optimized-ami/amazon-linux-2/recommended" +} + +module "autoscaling" { + source = "terraform-aws-modules/autoscaling/aws" + version = "~> 6.5" + + name = local.name + + image_id = jsondecode(data.aws_ssm_parameter.ecs_optimised_ami.value)["image_id"] + instance_type = "t3.micro" + ebs_optimized = true + enable_monitoring = true + + security_groups = [module.autoscaling_sg.security_group_id] + user_data = base64encode(local.user_data) + ignore_desired_capacity_changes = true + + iam_instance_profile_arn = module.ec2_profile.iam_instance_profile_arn + + vpc_zone_identifier = module.vpc.private_subnets + health_check_type = "EC2" + min_size = 0 + max_size = 2 + desired_capacity = 1 + + # https://github.com/hashicorp/terraform-provider-aws/issues/12582 + autoscaling_group_tags = { + AmazonECSManaged = true + } + + tags = local.tags +} + +module "autoscaling_sg" { + source = "terraform-aws-modules/security-group/aws" + version = "~> 4.0" + + name = local.name + description = "Autoscaling group security group" + vpc_id = module.vpc.vpc_id + + ingress_cidr_blocks = ["0.0.0.0/0"] + ingress_rules = ["https-443-tcp"] + + egress_rules = ["all-all"] + + tags = local.tags +} + +module "vpc" { + source = "terraform-aws-modules/vpc/aws" + version = "~> 3.0" + + name = local.name + cidr = "10.99.0.0/18" + + azs = ["${local.region}a", "${local.region}b", "${local.region}c"] + public_subnets = ["10.99.0.0/24", "10.99.1.0/24", "10.99.2.0/24"] + private_subnets = ["10.99.3.0/24", "10.99.4.0/24", "10.99.5.0/24"] + + enable_nat_gateway = true + single_nat_gateway = true + enable_dns_hostnames = true + map_public_ip_on_launch = false + + tags = local.tags +} +``` + +### After v4.x Example + +```hcl +provider "aws" { + region = local.region +} + +locals { + region = "eu-west-1" + name = "ecs-ex-${replace(basename(path.cwd), "_", "-")}" + + user_data = <<-EOT + #!/bin/bash + cat <<'EOF' >> /etc/ecs/ecs.config + ECS_CLUSTER=${local.name} + ECS_LOGLEVEL=debug + EOF + EOT + + tags = { + Name = local.name + Example = local.name + Repository = "https://github.com/terraform-aws-modules/terraform-aws-ecs" + } +} + +################################################################################ +# ECS Module +################################################################################ + +module "ecs" { + # source = "../../" + source = "../../../terraform-aws-ecs" + + cluster_name = local.name + + fargate_capacity_providers = { + "FARGATE" = {} + "FARGATE_SPOT" = {} + } + + autoscaling_capacity_providers = { + prov1 = { + auto_scaling_group_arn = module.autoscaling.autoscaling_group_arn + default_capacity_provider_strategy = { + weight = 1 + } + } + } + + tags = local.tags +} + +################################################################################ +# Supporting Resources +################################################################################ + +# https://docs.aws.amazon.com/AmazonECS/latest/developerguide/ecs-optimized_AMI.html#ecs-optimized-ami-linux +data "aws_ssm_parameter" "ecs_optimised_ami" { + name = "/aws/service/ecs/optimized-ami/amazon-linux-2/recommended" +} + +module "autoscaling" { + source = "terraform-aws-modules/autoscaling/aws" + version = "~> 6.5" + + name = local.name + + image_id = jsondecode(data.aws_ssm_parameter.ecs_optimised_ami.value)["image_id"] + instance_type = "t3.micro" + ebs_optimized = true + enable_monitoring = true + + security_groups = [module.autoscaling_sg.security_group_id] + user_data = base64encode(local.user_data) + ignore_desired_capacity_changes = true + + create_iam_instance_profile = true + iam_role_name = local.name + iam_role_policies = { + AmazonEC2ContainerServiceforEC2Role = "arn:aws:iam::aws:policy/service-role/AmazonEC2ContainerServiceforEC2Role" + CloudWatchLogsFullAccess = "arn:aws:iam::aws:policy/CloudWatchLogsFullAccess" + } + + vpc_zone_identifier = module.vpc.private_subnets + health_check_type = "EC2" + min_size = 0 + max_size = 2 + desired_capacity = 1 + + # https://github.com/hashicorp/terraform-provider-aws/issues/12582 + autoscaling_group_tags = { + AmazonECSManaged = true + } + + tags = local.tags +} + +module "autoscaling_sg" { + source = "terraform-aws-modules/security-group/aws" + version = "~> 4.0" + + name = local.name + description = "Autoscaling group security group" + vpc_id = module.vpc.vpc_id + + ingress_cidr_blocks = ["0.0.0.0/0"] + ingress_rules = ["https-443-tcp"] + + egress_rules = ["all-all"] + + tags = local.tags +} + +module "vpc" { + source = "terraform-aws-modules/vpc/aws" + version = "~> 3.0" + + name = local.name + cidr = "10.99.0.0/18" + + azs = ["${local.region}a", "${local.region}b", "${local.region}c"] + public_subnets = ["10.99.0.0/24", "10.99.1.0/24", "10.99.2.0/24"] + private_subnets = ["10.99.3.0/24", "10.99.4.0/24", "10.99.5.0/24"] + + enable_nat_gateway = true + single_nat_gateway = true + enable_dns_hostnames = true + map_public_ip_on_launch = false + + tags = local.tags +} +``` + +### Diff of Before vs After + +```diff +- module "ec2_profile" { +- source = "terraform-aws-modules/ecs/aws/modules/ecs-instance-profile" +- +- name = local.name +- } + +- resource "aws_ecs_capacity_provider" "prov1" { +- name = "prov1" +- +- auto_scaling_group_provider { +- auto_scaling_group_arn = module.autoscaling.autoscaling_group_arn +- } +- } + + module "ecs" { + source = "terraform-aws-modules/ecs/aws" +- version = "~> 3.0" ++ version = "~> 4.0" + +- name = local.name ++ cluster_name = local.name + +- container_insights = true ++ # On by default now + +- capacity_providers = ["FARGATE", "FARGATE_SPOT", aws_ecs_capacity_provider.prov1.name] +- default_capacity_provider_strategy = [{ +- capacity_provider = aws_ecs_capacity_provider.prov1.name +- weight = "1" +- }] + ++ fargate_capacity_providers = { ++ "FARGATE" = {} ++ "FARGATE_SPOT" = {} ++ } + ++ autoscaling_capacity_providers = { ++ prov1 = { ++ auto_scaling_group_arn = module.autoscaling.autoscaling_group_arn ++ default_capacity_provider_strategy = { ++ weight = 1 ++ } ++ } ++ } +} + +module "autoscaling" { + source = "terraform-aws-modules/autoscaling/aws" + version = "~> 6.5" + +- iam_instance_profile_arn = module.ec2_profile.iam_instance_profile_arn + ++ create_iam_instance_profile = true ++ iam_role_name = local.name ++ iam_role_policies = { ++ AmazonEC2ContainerServiceforEC2Role = "arn:aws:iam::aws:policy/service-role/AmazonEC2ContainerServiceforEC2Role" ++ CloudWatchLogsFullAccess = "arn:aws:iam::aws:policy/CloudWatchLogsFullAccess" ++ } +} +``` + +### State Move Commands + +The `terraform state mv ...` commands assocaited with the before and after changes shown above are as follows: + +```sh +# Cluster +terraform state mv 'aws_ecs_capacity_provider.prov1' 'module.ecs.aws_ecs_capacity_provider.this["prov1"]' + +# IAM instance profile +terraform state mv 'module.ec2_profile.aws_iam_role.this' 'module.autoscaling.aws_iam_role.this[0]' +terraform state mv 'module.ec2_profile.aws_iam_instance_profile.this' 'module.autoscaling.aws_iam_instance_profile.this[0]' +terraform state mv 'module.ec2_profile.aws_iam_role_policy_attachment.ecs_ec2_cloudwatch_role' 'module.autoscaling.aws_iam_role_policy_attachment.this["CloudWatchLogsFullAccess"]' +terraform state mv 'module.ec2_profile.aws_iam_role_policy_attachment.ecs_ec2_role' 'module.autoscaling.aws_iam_role_policy_attachment.this["AmazonEC2ContainerServiceforEC2Role"]' +``` diff --git a/examples/ec2/service-hello-world/main.tf b/examples/ec2/service-hello-world/main.tf index caea1dd..b9661bb 100644 --- a/examples/ec2/service-hello-world/main.tf +++ b/examples/ec2/service-hello-world/main.tf @@ -18,7 +18,7 @@ resource "aws_ecs_task_definition" "this" { "options": { "awslogs-region": "eu-west-1", "awslogs-group": "${aws_cloudwatch_log_group.this.name}", - "awslogs-stream-prefix": "fargate" + "awslogs-stream-prefix": "ec2" } } } diff --git a/main.tf b/main.tf index ed641e1..881f0ff 100644 --- a/main.tf +++ b/main.tf @@ -72,11 +72,12 @@ resource "aws_ecs_cluster_capacity_providers" "this" { dynamic "default_capacity_provider_strategy" { for_each = local.cluster_capacity_providers + iterator = strategy content { - capacity_provider = try(default_capacity_provider_strategy.value.name, default_capacity_provider_strategy.key) - base = try(default_capacity_provider_strategy.value.default_capacity_provider_strategy.base, null) - weight = try(default_capacity_provider_strategy.value.default_capacity_provider_strategy.weight, null) + capacity_provider = try(strategy.value.name, strategy.key) + base = try(strategy.value.default_capacity_provider_strategy.base, null) + weight = try(strategy.value.default_capacity_provider_strategy.weight, null) } } } @@ -92,7 +93,7 @@ resource "aws_ecs_capacity_provider" "this" { auto_scaling_group_provider { auto_scaling_group_arn = each.value.auto_scaling_group_arn - managed_termination_protection = each.value.managed_termination_protection + managed_termination_protection = try(each.value.managed_termination_protection, null) dynamic "managed_scaling" { for_each = try([each.value.managed_scaling], []) From 4febc6c021fef98f1a58f21a510305eaadb1340b Mon Sep 17 00:00:00 2001 From: Bryant Biggs Date: Fri, 3 Jun 2022 20:36:51 -0400 Subject: [PATCH 5/7] chore: For upgrade path, users can use v3.5.0 of instance profile to avoid disruption --- UPGRADE-4.0.md | 270 ++++--------------------------------------------- 1 file changed, 20 insertions(+), 250 deletions(-) diff --git a/UPGRADE-4.0.md b/UPGRADE-4.0.md index b07d64e..aafe81b 100644 --- a/UPGRADE-4.0.md +++ b/UPGRADE-4.0.md @@ -88,55 +88,26 @@ Where the current equivalent now looks like: ### Before v3.x Example ```hcl -provider "aws" { - region = local.region -} - -locals { - region = "eu-west-1" - name = "ecs-ex-${replace(basename(path.cwd), "_", "-")}" - - user_data = <<-EOT - #!/bin/bash - cat <<'EOF' >> /etc/ecs/ecs.config - ECS_CLUSTER=${local.name} - ECS_LOGLEVEL=debug - EOF - EOT - - tags = { - Name = local.name - Example = local.name - Repository = "https://github.com/terraform-aws-modules/terraform-aws-ecs" - } -} - -################################################################################ -# ECS Module -################################################################################ - module "ecs" { - source = "../../" + source = "terraform-aws-modules/ecs/aws" + version = "3.5.0" - name = local.name + name = "example" container_insights = true capacity_providers = ["FARGATE", "FARGATE_SPOT", aws_ecs_capacity_provider.prov1.name] default_capacity_provider_strategy = [{ - capacity_provider = aws_ecs_capacity_provider.prov1.name # "FARGATE_SPOT" + capacity_provider = aws_ecs_capacity_provider.prov1.name weight = "1" }] - - tags = local.tags } module "ec2_profile" { - source = "../../modules/ecs-instance-profile" + source = "terraform-aws-modules/ecs/aws//modules/ecs-instance-profile" + version = "3.5.0" name = local.name - - tags = local.tags } resource "aws_ecs_capacity_provider" "prov1" { @@ -146,118 +117,16 @@ resource "aws_ecs_capacity_provider" "prov1" { auto_scaling_group_arn = module.autoscaling.autoscaling_group_arn } } - -################################################################################ -# Supporting Resources -################################################################################ - -# https://docs.aws.amazon.com/AmazonECS/latest/developerguide/ecs-optimized_AMI.html#ecs-optimized-ami-linux -data "aws_ssm_parameter" "ecs_optimised_ami" { - name = "/aws/service/ecs/optimized-ami/amazon-linux-2/recommended" -} - -module "autoscaling" { - source = "terraform-aws-modules/autoscaling/aws" - version = "~> 6.5" - - name = local.name - - image_id = jsondecode(data.aws_ssm_parameter.ecs_optimised_ami.value)["image_id"] - instance_type = "t3.micro" - ebs_optimized = true - enable_monitoring = true - - security_groups = [module.autoscaling_sg.security_group_id] - user_data = base64encode(local.user_data) - ignore_desired_capacity_changes = true - - iam_instance_profile_arn = module.ec2_profile.iam_instance_profile_arn - - vpc_zone_identifier = module.vpc.private_subnets - health_check_type = "EC2" - min_size = 0 - max_size = 2 - desired_capacity = 1 - - # https://github.com/hashicorp/terraform-provider-aws/issues/12582 - autoscaling_group_tags = { - AmazonECSManaged = true - } - - tags = local.tags -} - -module "autoscaling_sg" { - source = "terraform-aws-modules/security-group/aws" - version = "~> 4.0" - - name = local.name - description = "Autoscaling group security group" - vpc_id = module.vpc.vpc_id - - ingress_cidr_blocks = ["0.0.0.0/0"] - ingress_rules = ["https-443-tcp"] - - egress_rules = ["all-all"] - - tags = local.tags -} - -module "vpc" { - source = "terraform-aws-modules/vpc/aws" - version = "~> 3.0" - - name = local.name - cidr = "10.99.0.0/18" - - azs = ["${local.region}a", "${local.region}b", "${local.region}c"] - public_subnets = ["10.99.0.0/24", "10.99.1.0/24", "10.99.2.0/24"] - private_subnets = ["10.99.3.0/24", "10.99.4.0/24", "10.99.5.0/24"] - - enable_nat_gateway = true - single_nat_gateway = true - enable_dns_hostnames = true - map_public_ip_on_launch = false - - tags = local.tags -} ``` ### After v4.x Example ```hcl -provider "aws" { - region = local.region -} - -locals { - region = "eu-west-1" - name = "ecs-ex-${replace(basename(path.cwd), "_", "-")}" - - user_data = <<-EOT - #!/bin/bash - cat <<'EOF' >> /etc/ecs/ecs.config - ECS_CLUSTER=${local.name} - ECS_LOGLEVEL=debug - EOF - EOT - - tags = { - Name = local.name - Example = local.name - Repository = "https://github.com/terraform-aws-modules/terraform-aws-ecs" - } -} - -################################################################################ -# ECS Module -################################################################################ - module "ecs" { - # source = "../../" - source = "../../../terraform-aws-ecs" + source = "terraform-aws-modules/ecs/aws" + version = "4.0.0" - cluster_name = local.name + cluster_name = "example" fargate_capacity_providers = { "FARGATE" = {} @@ -272,100 +141,21 @@ module "ecs" { } } } - - tags = local.tags -} - -################################################################################ -# Supporting Resources -################################################################################ - -# https://docs.aws.amazon.com/AmazonECS/latest/developerguide/ecs-optimized_AMI.html#ecs-optimized-ami-linux -data "aws_ssm_parameter" "ecs_optimised_ami" { - name = "/aws/service/ecs/optimized-ami/amazon-linux-2/recommended" -} - -module "autoscaling" { - source = "terraform-aws-modules/autoscaling/aws" - version = "~> 6.5" - - name = local.name - - image_id = jsondecode(data.aws_ssm_parameter.ecs_optimised_ami.value)["image_id"] - instance_type = "t3.micro" - ebs_optimized = true - enable_monitoring = true - - security_groups = [module.autoscaling_sg.security_group_id] - user_data = base64encode(local.user_data) - ignore_desired_capacity_changes = true - - create_iam_instance_profile = true - iam_role_name = local.name - iam_role_policies = { - AmazonEC2ContainerServiceforEC2Role = "arn:aws:iam::aws:policy/service-role/AmazonEC2ContainerServiceforEC2Role" - CloudWatchLogsFullAccess = "arn:aws:iam::aws:policy/CloudWatchLogsFullAccess" - } - - vpc_zone_identifier = module.vpc.private_subnets - health_check_type = "EC2" - min_size = 0 - max_size = 2 - desired_capacity = 1 - - # https://github.com/hashicorp/terraform-provider-aws/issues/12582 - autoscaling_group_tags = { - AmazonECSManaged = true - } - - tags = local.tags -} - -module "autoscaling_sg" { - source = "terraform-aws-modules/security-group/aws" - version = "~> 4.0" - - name = local.name - description = "Autoscaling group security group" - vpc_id = module.vpc.vpc_id - - ingress_cidr_blocks = ["0.0.0.0/0"] - ingress_rules = ["https-443-tcp"] - - egress_rules = ["all-all"] - - tags = local.tags } -module "vpc" { - source = "terraform-aws-modules/vpc/aws" - version = "~> 3.0" - - name = local.name - cidr = "10.99.0.0/18" - - azs = ["${local.region}a", "${local.region}b", "${local.region}c"] - public_subnets = ["10.99.0.0/24", "10.99.1.0/24", "10.99.2.0/24"] - private_subnets = ["10.99.3.0/24", "10.99.4.0/24", "10.99.5.0/24"] - - enable_nat_gateway = true - single_nat_gateway = true - enable_dns_hostnames = true - map_public_ip_on_launch = false +module "ec2_profile" { + source = "terraform-aws-modules/ecs/aws//modules/ecs-instance-profile" + # Users can pin and stay on v3.5.0 until they able to use the IAM instance + # profile provided through the autoscaling group module + version = "3.5.0" - tags = local.tags + name = "example } ``` ### Diff of Before vs After ```diff -- module "ec2_profile" { -- source = "terraform-aws-modules/ecs/aws/modules/ecs-instance-profile" -- -- name = local.name -- } - - resource "aws_ecs_capacity_provider" "prov1" { - name = "prov1" - @@ -376,11 +166,11 @@ module "vpc" { module "ecs" { source = "terraform-aws-modules/ecs/aws" -- version = "~> 3.0" -+ version = "~> 4.0" +- version = "3.5.0" ++ version = "4.0.0" -- name = local.name -+ cluster_name = local.name +- name = "example" ++ cluster_name = "example" - container_insights = true + # On by default now @@ -405,33 +195,13 @@ module "vpc" { + } + } } - -module "autoscaling" { - source = "terraform-aws-modules/autoscaling/aws" - version = "~> 6.5" - -- iam_instance_profile_arn = module.ec2_profile.iam_instance_profile_arn - -+ create_iam_instance_profile = true -+ iam_role_name = local.name -+ iam_role_policies = { -+ AmazonEC2ContainerServiceforEC2Role = "arn:aws:iam::aws:policy/service-role/AmazonEC2ContainerServiceforEC2Role" -+ CloudWatchLogsFullAccess = "arn:aws:iam::aws:policy/CloudWatchLogsFullAccess" -+ } -} ``` ### State Move Commands -The `terraform state mv ...` commands assocaited with the before and after changes shown above are as follows: +In conjunction with the changes above, users can elect to move their external capacity provider(s) under this module using the following move command. Command is shown using the values from the example shown above, please update to suit your configuration names: ```sh # Cluster terraform state mv 'aws_ecs_capacity_provider.prov1' 'module.ecs.aws_ecs_capacity_provider.this["prov1"]' - -# IAM instance profile -terraform state mv 'module.ec2_profile.aws_iam_role.this' 'module.autoscaling.aws_iam_role.this[0]' -terraform state mv 'module.ec2_profile.aws_iam_instance_profile.this' 'module.autoscaling.aws_iam_instance_profile.this[0]' -terraform state mv 'module.ec2_profile.aws_iam_role_policy_attachment.ecs_ec2_cloudwatch_role' 'module.autoscaling.aws_iam_role_policy_attachment.this["CloudWatchLogsFullAccess"]' -terraform state mv 'module.ec2_profile.aws_iam_role_policy_attachment.ecs_ec2_role' 'module.autoscaling.aws_iam_role_policy_attachment.this["AmazonEC2ContainerServiceforEC2Role"]' ``` From 01564cb6e1353cf4d6ebd3344b2b3945f32edfe9 Mon Sep 17 00:00:00 2001 From: Bryant Biggs Date: Tue, 7 Jun 2022 08:22:59 -0400 Subject: [PATCH 6/7] chore: Update based on PR feedback --- README.md | 10 +++---- UPGRADE-4.0.md | 12 ++++---- examples/{ec2 => complete}/README.md | 2 +- examples/{ec2 => complete}/main.tf | 30 +++++++------------ examples/{ec2 => complete}/outputs.tf | 0 .../service-hello-world/main.tf | 0 .../service-hello-world/outputs.tf | 0 .../service-hello-world/variables.tf | 0 .../service-hello-world/versions.tf | 0 examples/{ec2 => complete}/variables.tf | 0 examples/{ec2 => complete}/versions.tf | 0 examples/fargate/main.tf | 16 +++++----- main.tf | 4 +-- outputs.tf | 4 ++- 14 files changed, 35 insertions(+), 43 deletions(-) rename examples/{ec2 => complete}/README.md (97%) rename examples/{ec2 => complete}/main.tf (88%) rename examples/{ec2 => complete}/outputs.tf (100%) rename examples/{ec2 => complete}/service-hello-world/main.tf (100%) rename examples/{ec2 => complete}/service-hello-world/outputs.tf (100%) rename examples/{ec2 => complete}/service-hello-world/variables.tf (100%) rename examples/{ec2 => complete}/service-hello-world/versions.tf (100%) rename examples/{ec2 => complete}/variables.tf (100%) rename examples/{ec2 => complete}/versions.tf (100%) diff --git a/README.md b/README.md index b150a50..8bc914b 100644 --- a/README.md +++ b/README.md @@ -28,12 +28,12 @@ module "ecs" { } fargate_capacity_providers = { - "FARGATE" = { + FARGATE = { default_capacity_provider_strategy = { weight = 50 } } - "FARGATE_SPOT" = { + FARGATE_SPOT = { default_capacity_provider_strategy = { weight = 50 } @@ -123,12 +123,12 @@ module "ecs" { } fargate_capacity_providers = { - "FARGATE" = { + FARGATE = { default_capacity_provider_strategy = { weight = 50 } } - "FARGATE_SPOT" = { + FARGATE_SPOT = { default_capacity_provider_strategy = { weight = 50 } @@ -193,7 +193,7 @@ module "ecs" { ## Examples -- [ECS Cluster w/ EC2 Autoscaling Capacity Provider](https://github.com/terraform-aws-modules/terraform-aws-ecs/tree/master/examples/ec2) +- [ECS Cluster w/ EC2 Autoscaling Capacity Provider](https://github.com/terraform-aws-modules/terraform-aws-ecs/tree/master/examples/complete) - [ECS Cluster w/ Fargate Capacity Provider](https://github.com/terraform-aws-modules/terraform-aws-ecs/tree/master/examples/fargate) diff --git a/UPGRADE-4.0.md b/UPGRADE-4.0.md index aafe81b..8e0b9d1 100644 --- a/UPGRADE-4.0.md +++ b/UPGRADE-4.0.md @@ -35,13 +35,13 @@ Please consult the `examples` directory for reference example configurations. If Where the current equivalent now looks like: ```hcl fargate_capacity_providers = { - "FARGATE" = { + FARGATE = { default_capacity_provider_strategy = { weight = 50 base = 20 } } - "FARGATE_SPOT" = { + FARGATE_SPOT = { default_capacity_provider_strategy = { weight = 50 } @@ -129,8 +129,8 @@ module "ecs" { cluster_name = "example" fargate_capacity_providers = { - "FARGATE" = {} - "FARGATE_SPOT" = {} + FARGATE = {} + FARGATE_SPOT = {} } autoscaling_capacity_providers = { @@ -182,8 +182,8 @@ module "ec2_profile" { - }] + fargate_capacity_providers = { -+ "FARGATE" = {} -+ "FARGATE_SPOT" = {} ++ FARGATE = {} ++ FARGATE_SPOT = {} + } + autoscaling_capacity_providers = { diff --git a/examples/ec2/README.md b/examples/complete/README.md similarity index 97% rename from examples/ec2/README.md rename to examples/complete/README.md index 5df64b0..15052e6 100644 --- a/examples/ec2/README.md +++ b/examples/complete/README.md @@ -48,7 +48,7 @@ Note that this example may create resources which will incur monetary charges on | Name | Type | |------|------| | [aws_cloudwatch_log_group.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudwatch_log_group) | resource | -| [aws_ssm_parameter.ecs_optimised_ami](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/ssm_parameter) | data source | +| [aws_ssm_parameter.ecs_optimized_ami](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/ssm_parameter) | data source | ## Inputs diff --git a/examples/ec2/main.tf b/examples/complete/main.tf similarity index 88% rename from examples/ec2/main.tf rename to examples/complete/main.tf index 7c37ad2..d589b07 100644 --- a/examples/ec2/main.tf +++ b/examples/complete/main.tf @@ -25,12 +25,6 @@ locals { # ECS Module ################################################################################ -module "ecs_disabled" { - source = "../.." - - create = false -} - module "ecs" { source = "../.." @@ -92,12 +86,18 @@ module "hello_world" { cluster_id = module.ecs.cluster_id } +module "ecs_disabled" { + source = "../.." + + create = false +} + ################################################################################ # Supporting Resources ################################################################################ # https://docs.aws.amazon.com/AmazonECS/latest/developerguide/ecs-optimized_AMI.html#ecs-optimized-ami-linux -data "aws_ssm_parameter" "ecs_optimised_ami" { +data "aws_ssm_parameter" "ecs_optimized_ami" { name = "/aws/service/ecs/optimized-ami/amazon-linux-2/recommended" } @@ -116,10 +116,8 @@ module "autoscaling" { name = "${local.name}-${each.key}" - image_id = jsondecode(data.aws_ssm_parameter.ecs_optimised_ami.value)["image_id"] - instance_type = each.value.instance_type - ebs_optimized = true - enable_monitoring = true + image_id = jsondecode(data.aws_ssm_parameter.ecs_optimized_ami.value)["image_id"] + instance_type = each.value.instance_type security_groups = [module.autoscaling_sg.security_group_id] user_data = base64encode(local.user_data) @@ -177,19 +175,11 @@ module "vpc" { public_subnets = ["10.99.0.0/24", "10.99.1.0/24", "10.99.2.0/24"] private_subnets = ["10.99.3.0/24", "10.99.4.0/24", "10.99.5.0/24"] - enable_nat_gateway = true + enable_nat_gateway = false single_nat_gateway = true enable_dns_hostnames = true map_public_ip_on_launch = false - # Manage so we can name - manage_default_network_acl = true - default_network_acl_tags = { Name = "${local.name}-default" } - manage_default_route_table = true - default_route_table_tags = { Name = "${local.name}-default" } - manage_default_security_group = true - default_security_group_tags = { Name = "${local.name}-default" } - tags = local.tags } diff --git a/examples/ec2/outputs.tf b/examples/complete/outputs.tf similarity index 100% rename from examples/ec2/outputs.tf rename to examples/complete/outputs.tf diff --git a/examples/ec2/service-hello-world/main.tf b/examples/complete/service-hello-world/main.tf similarity index 100% rename from examples/ec2/service-hello-world/main.tf rename to examples/complete/service-hello-world/main.tf diff --git a/examples/ec2/service-hello-world/outputs.tf b/examples/complete/service-hello-world/outputs.tf similarity index 100% rename from examples/ec2/service-hello-world/outputs.tf rename to examples/complete/service-hello-world/outputs.tf diff --git a/examples/ec2/service-hello-world/variables.tf b/examples/complete/service-hello-world/variables.tf similarity index 100% rename from examples/ec2/service-hello-world/variables.tf rename to examples/complete/service-hello-world/variables.tf diff --git a/examples/ec2/service-hello-world/versions.tf b/examples/complete/service-hello-world/versions.tf similarity index 100% rename from examples/ec2/service-hello-world/versions.tf rename to examples/complete/service-hello-world/versions.tf diff --git a/examples/ec2/variables.tf b/examples/complete/variables.tf similarity index 100% rename from examples/ec2/variables.tf rename to examples/complete/variables.tf diff --git a/examples/ec2/versions.tf b/examples/complete/versions.tf similarity index 100% rename from examples/ec2/versions.tf rename to examples/complete/versions.tf diff --git a/examples/fargate/main.tf b/examples/fargate/main.tf index 42d8619..f800c3e 100644 --- a/examples/fargate/main.tf +++ b/examples/fargate/main.tf @@ -17,12 +17,6 @@ locals { # Ecs Module ################################################################################ -module "ecs_disabled" { - source = "../.." - - create = false -} - module "ecs" { source = "../.." @@ -42,13 +36,13 @@ module "ecs" { # Capacity provider fargate_capacity_providers = { - "FARGATE" = { + FARGATE = { default_capacity_provider_strategy = { weight = 50 base = 20 } } - "FARGATE_SPOT" = { + FARGATE_SPOT = { default_capacity_provider_strategy = { weight = 50 } @@ -58,6 +52,12 @@ module "ecs" { tags = local.tags } +module "ecs_disabled" { + source = "../.." + + create = false +} + ################################################################################ # Supporting Resources ################################################################################ diff --git a/main.tf b/main.tf index 881f0ff..b75b4ff 100644 --- a/main.tf +++ b/main.tf @@ -35,10 +35,10 @@ resource "aws_ecs_cluster" "this" { } dynamic "setting" { - for_each = length(var.cluster_settings) > 0 ? [var.cluster_settings] : [] + for_each = [var.cluster_settings] content { - name = try(setting.value.name, "containerInsights") + name = setting.value.name value = setting.value.value } } diff --git a/outputs.tf b/outputs.tf index dbedbdf..d0368ac 100644 --- a/outputs.tf +++ b/outputs.tf @@ -18,7 +18,9 @@ output "cluster_id" { output "cluster_capacity_providers" { description = "Map of cluster capacity providers attributes" - value = aws_ecs_cluster_capacity_providers.this + value = { + for k, v in aws_ecs_cluster_capacity_providers.this : v.id => v + } } ################################################################################ From 6d9c5a22c321c02ddbb4c6ea3e0df557e911e6dc Mon Sep 17 00:00:00 2001 From: Bryant Biggs Date: Tue, 7 Jun 2022 08:24:49 -0400 Subject: [PATCH 7/7] fix: Correct links for directory rename --- UPGRADE-4.0.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/UPGRADE-4.0.md b/UPGRADE-4.0.md index 8e0b9d1..a1bf05d 100644 --- a/UPGRADE-4.0.md +++ b/UPGRADE-4.0.md @@ -6,7 +6,7 @@ Please consult the `examples` directory for reference example configurations. If - Minimum supported version of Terraform AWS provider updated to v4.6 to support the latest resources utilized - Minimum supported version of Terraform updated to v1.0 -- `ecs-instance-profile` sub-module has been removed; this functionality is available through the [`terraform-aws-modules/terraform-aws-autoscaling`](https://github.com/terraform-aws-modules/terraform-aws-autoscaling) module starting with version [v6.5.0](https://github.com/terraform-aws-modules/terraform-aws-autoscaling/pull/194). Please see the [`examples/ec2`](https://github.com/terraform-aws-modules/terraform-aws-ecs/tree/master/examples/ec2) example for a demonstration on how to use and integrate with the `terraform-aws-autoscaling` module. +- `ecs-instance-profile` sub-module has been removed; this functionality is available through the [`terraform-aws-modules/terraform-aws-autoscaling`](https://github.com/terraform-aws-modules/terraform-aws-autoscaling) module starting with version [v6.5.0](https://github.com/terraform-aws-modules/terraform-aws-autoscaling/pull/194). Please see the [`examples/complete`](https://github.com/terraform-aws-modules/terraform-aws-ecs/tree/master/examples/complete) example for a demonstration on how to use and integrate with the `terraform-aws-autoscaling` module. - The `container_insights` and `capacity_providers` variables have been replaced by new variables - see below for more details ## Additional changes @@ -52,7 +52,7 @@ Where the current equivalent now looks like: ### Removed -- `ecs-instance-profile` sub-module has been removed; this functionality is available through the [`terraform-aws-modules/terraform-aws-autoscaling`](https://github.com/terraform-aws-modules/terraform-aws-autoscaling) module starting with version [v6.5.0](https://github.com/terraform-aws-modules/terraform-aws-autoscaling/pull/194). Please see the [`examples/ec2`](https://github.com/terraform-aws-modules/terraform-aws-ecs/tree/master/examples/ec2) example for a demonstration on how to use and integrate with the `terraform-aws-autoscaling` module. +- `ecs-instance-profile` sub-module has been removed; this functionality is available through the [`terraform-aws-modules/terraform-aws-autoscaling`](https://github.com/terraform-aws-modules/terraform-aws-autoscaling) module starting with version [v6.5.0](https://github.com/terraform-aws-modules/terraform-aws-autoscaling/pull/194). Please see the [`examples/complete`](https://github.com/terraform-aws-modules/terraform-aws-ecs/tree/master/examples/complete) example for a demonstration on how to use and integrate with the `terraform-aws-autoscaling` module. ### Variable and output changes