diff --git a/.gitignore b/.gitignore index 4bcc238..b6abc3c 100644 --- a/.gitignore +++ b/.gitignore @@ -9,6 +9,3 @@ .build-harness build-harness - -# Terraform tfvars files -*.tfvars diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 241026e..0000000 --- a/.travis.yml +++ /dev/null @@ -1,16 +0,0 @@ -addons: - apt: - packages: - - git - - make - - curl - -install: - - make init - -script: - - make terraform/install - - make terraform/get-plugins - - make terraform/get-modules - - make terraform/lint - - make terraform/validate diff --git a/README.md b/README.md index 2042cc7..4c88474 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ [![Cloud Posse][logo]](https://cpco.io/homepage) -# terraform-aws-ecs-alb-service-task [![Build Status](https://travis-ci.org/cloudposse/terraform-aws-ecs-alb-service-task.svg?branch=master)](https://travis-ci.org/cloudposse/terraform-aws-ecs-alb-service-task) [![Latest Release](https://img.shields.io/github/release/cloudposse/terraform-aws-ecs-alb-service-task.svg)](https://github.com/cloudposse/terraform-aws-ecs-alb-service-task/releases/latest) [![Slack Community](https://slack.cloudposse.com/badge.svg)](https://slack.cloudposse.com) +# terraform-aws-ecs-alb-service-task [![Codefresh Build Status](https://g.codefresh.io/api/badges/pipeline/cloudposse/terraform-modules%2Fterraform-aws-ecs-alb-service-task?type=cf-1)](https://g.codefresh.io/public/accounts/cloudposse/pipelines/5db352c10c7c5a56af1de612) [![Latest Release](https://img.shields.io/github/release/cloudposse/terraform-aws-ecs-alb-service-task.svg)](https://github.com/cloudposse/terraform-aws-ecs-alb-service-task/releases/latest) [![Slack Community](https://slack.cloudposse.com/badge.svg)](https://slack.cloudposse.com) Terraform module to create an ECS Service for a web app (task), and an ALB target group to route requests. @@ -48,68 +48,98 @@ Instead pin to the release tag (e.g. `?ref=tags/x.y.z`) of one of our [latest re -For a complete example, see [examples/complete](examples/complete) +For a complete example, see [examples/complete](examples/complete). + +For automated test of the complete example using `bats` and `Terratest`, see [test](test). ```hcl -module "label" { - source = "git::https://github.com/cloudposse/terraform-terraform-label.git?ref=master" - namespace = "eg" - stage = "staging" - name = "app" -} - -module "container_definition" { - source = "git::https://github.com/cloudposse/terraform-aws-ecs-container-definition.git?ref=master" - container_name = "app" - container_image = "cloudposse/geodesic:latest" - - environment = [ - { - name = "string_var" - value = "I am a string" - }, - { - name = "true_boolean_var" - value = true - }, - { - name = "false_boolean_var" - value = false - }, - { - name = "integer_var" - value = 42 - }, - ] - - port_mappings = [ - { - containerPort = 8080 - hostPort = 80 - protocol = "tcp" - }, - { - containerPort = 8081 - hostPort = 443 - protocol = "udp" - }, - ] -} - -module "alb_service_task" { - source = "git::https://github.com/cloudposse/terraform-aws-ecs-alb-service-task.git?ref=master" - namespace = "eg" - stage = "staging" - name = "app" - alb_target_group_arn = "xxxxxxx" - container_definition_json = "${module.container_definition.json}" - container_name = "${module.label.id}" - ecs_cluster_arn = "xxxxxxx" - launch_type = "FARGATE" - vpc_id = "xxxxxxx" - security_group_ids = ["xxxxx", "yyyyy"] - private_subnet_ids = ["xxxxx", "yyyyy", "zzzzz"] -} + provider "aws" { + region = var.region + } + + module "label" { + source = "git::https://github.com/cloudposse/terraform-null-label.git?ref=tags/0.15.0" + namespace = var.namespace + name = var.name + stage = var.stage + delimiter = var.delimiter + attributes = var.attributes + tags = var.tags + } + + module "vpc" { + source = "git::https://github.com/cloudposse/terraform-aws-vpc.git?ref=tags/0.8.1" + namespace = var.namespace + stage = var.stage + name = var.name + delimiter = var.delimiter + attributes = var.attributes + cidr_block = var.vpc_cidr_block + tags = var.tags + } + + module "subnets" { + source = "git::https://github.com/cloudposse/terraform-aws-dynamic-subnets.git?ref=tags/0.16.1" + availability_zones = var.availability_zones + namespace = var.namespace + stage = var.stage + name = var.name + attributes = var.attributes + delimiter = var.delimiter + vpc_id = module.vpc.vpc_id + igw_id = module.vpc.igw_id + cidr_block = module.vpc.vpc_cidr_block + nat_gateway_enabled = true + nat_instance_enabled = false + tags = var.tags + } + + resource "aws_ecs_cluster" "default" { + name = module.label.id + tags = module.label.tags + } + + module "container_definition" { + source = "git::https://github.com/cloudposse/terraform-aws-ecs-container-definition.git?ref=tags/0.21.0" + container_name = var.container_name + container_image = var.container_image + container_memory = var.container_memory + container_memory_reservation = var.container_memory_reservation + container_cpu = var.container_cpu + essential = var.container_essential + readonly_root_filesystem = var.container_readonly_root_filesystem + environment = var.container_environment + port_mappings = var.container_port_mappings + log_configuration = var.container_log_configuration + } + + module "ecs_alb_service_task" { + source = "git::https://github.com/cloudposse/terraform-aws-ecs-alb-service-task.git?ref=master" + namespace = var.namespace + stage = var.stage + name = var.name + attributes = var.attributes + delimiter = var.delimiter + alb_security_group = module.vpc.vpc_default_security_group_id + container_definition_json = module.container_definition.json + ecs_cluster_arn = aws_ecs_cluster.default.arn + launch_type = var.ecs_launch_type + vpc_id = module.vpc.vpc_id + security_group_ids = [module.vpc.vpc_default_security_group_id] + subnet_ids = module.subnets.public_subnet_ids + tags = var.tags + ignore_changes_task_definition = var.ignore_changes_task_definition + network_mode = var.network_mode + assign_public_ip = var.assign_public_ip + propagate_tags = var.propagate_tags + health_check_grace_period_seconds = var.health_check_grace_period_seconds + deployment_minimum_healthy_percent = var.deployment_minimum_healthy_percent + deployment_maximum_percent = var.deployment_maximum_percent + deployment_controller_type = var.deployment_controller_type + desired_count = var.desired_count + task_memory = var.task_memory + task_cpu = var.task_cpu + } ``` The `container_image` in the `container_definition` module is the Docker image used to start a container. @@ -152,38 +182,39 @@ Available targets: | Name | Description | Type | Default | Required | |------|-------------|:----:|:-----:|:-----:| | alb_security_group | Security group of the ALB | string | - | yes | -| assign_public_ip | Assign a public IP address to the ENI (Fargate launch type only). Valid values are true or false. Default false. | string | `false` | no | -| attributes | Additional attributes (e.g. `1`) | list | `` | no | +| assign_public_ip | Assign a public IP address to the ENI (Fargate launch type only). Valid values are `true` or `false`. Default `false` | bool | `false` | no | +| attributes | Additional attributes (_e.g._ "1") | list(string) | `` | no | | container_definition_json | The JSON of the task container definition | string | - | yes | -| container_port | The port on the container to allow via the ingress security group | string | `80` | no | -| delimiter | Delimiter to be used between `name`, `namespace`, `stage`, etc. | string | `-` | no | -| deployment_controller_type | Type of deployment controller. Valid values: `CODE_DEPLOY`, `ECS`. | string | `ECS` | no | -| deployment_maximum_percent | The upper limit of the number of tasks (as a percentage of `desired_count`) that can be running in a service during a deployment | string | `200` | no | -| deployment_minimum_healthy_percent | The lower limit (as a percentage of `desired_count`) of the number of tasks that must remain running and healthy in a service during a deployment | string | `100` | no | -| desired_count | The number of instances of the task definition to place and keep running | string | `1` | no | +| container_port | The port on the container to allow via the ingress security group | number | `80` | no | +| delimiter | Delimiter between `namespace`, `stage`, `name` and `attributes` | string | `-` | no | +| deployment_controller_type | Type of deployment controller. Valid values are `CODE_DEPLOY` and `ECS` | string | `ECS` | no | +| deployment_maximum_percent | The upper limit of the number of tasks (as a percentage of `desired_count`) that can be running in a service during a deployment | number | `200` | no | +| deployment_minimum_healthy_percent | The lower limit (as a percentage of `desired_count`) of the number of tasks that must remain running and healthy in a service during a deployment | number | `100` | no | +| desired_count | The number of instances of the task definition to place and keep running | number | `1` | no | | ecs_cluster_arn | The ARN of the ECS cluster where service will be provisioned | string | - | yes | -| ecs_load_balancers | A list of load balancer config objects for the ECS service; see `load_balancer` docs https://www.terraform.io/docs/providers/aws/r/ecs_service.html | list | `` | no | -| health_check_grace_period_seconds | Seconds to ignore failing load balancer health checks on newly instantiated tasks to prevent premature shutdown, up to 7200. Only valid for services configured to use load balancers | string | `0` | no | -| ignore_changes_task_definition | Whether to ignore changes in container definition and task definition in the ECS service | string | `true` | no | +| ecs_load_balancers | A list of load balancer config objects for the ECS service; see `load_balancer` docs https://www.terraform.io/docs/providers/aws/r/ecs_service.html | object | `` | no | +| enabled | Set to false to prevent the module from creating any resources | bool | `true` | no | +| health_check_grace_period_seconds | Seconds to ignore failing load balancer health checks on newly instantiated tasks to prevent premature shutdown, up to 7200. Only valid for services configured to use load balancers | number | `0` | no | +| ignore_changes_task_definition | Whether to ignore changes in container definition and task definition in the ECS service | bool | `true` | no | | launch_type | The launch type on which to run your service. Valid values are `EC2` and `FARGATE` | string | `FARGATE` | no | -| name | Solution name, e.g. 'app' or 'cluster' | string | - | yes | -| namespace | Namespace, which could be your organization name, e.g. 'eg' or 'cp' | string | - | yes | -| network_mode | The network mode to use for the task. This is required to be awsvpc for `FARGATE` `launch_type` | string | `awsvpc` | no | -| propagate_tags | Specifies whether to propagate the tags from the task definition or the service to the tasks. The valid values are SERVICE and TASK_DEFINITION. | string | `` | no | -| security_group_ids | Security group IDs to allow in Service `network_configuration` | list | - | yes | -| stage | Stage, e.g. 'prod', 'staging', 'dev', or 'test' | string | - | yes | -| subnet_ids | Subnet IDs | list | - | yes | -| tags | Additional tags (e.g. `map('BusinessUnit`,`XYZ`) | map | `` | no | -| task_cpu | The number of CPU units used by the task. If using `FARGATE` launch type `task_cpu` must match supported memory values (https://docs.aws.amazon.com/AmazonECS/latest/developerguide/task_definition_parameters.html#task_size) | string | `256` | no | -| task_memory | The amount of memory (in MiB) used by the task. If using Fargate launch type `task_memory` must match supported cpu value (https://docs.aws.amazon.com/AmazonECS/latest/developerguide/task_definition_parameters.html#task_size) | string | `512` | no | -| volumes | Task volume definitions as list of maps | list | `` | no | +| name | Name of the application | string | - | yes | +| namespace | Namespace (e.g. `eg` or `cp`) | string | `` | no | +| network_mode | The network mode to use for the task. This is required to be `awsvpc` for `FARGATE` `launch_type` | string | `awsvpc` | no | +| propagate_tags | Specifies whether to propagate the tags from the task definition or the service to the tasks. The valid values are SERVICE and TASK_DEFINITION | string | `null` | no | +| security_group_ids | Security group IDs to allow in Service `network_configuration` | list(string) | `` | no | +| stage | Stage (e.g. `prod`, `dev`, `staging`) | string | `` | no | +| subnet_ids | Subnet IDs | list(string) | - | yes | +| tags | Additional tags (_e.g._ { BusinessUnit : ABC }) | map(string) | `` | no | +| task_cpu | The number of CPU units used by the task. If using `FARGATE` launch type `task_cpu` must match supported memory values (https://docs.aws.amazon.com/AmazonECS/latest/developerguide/task_definition_parameters.html#task_size) | number | `256` | no | +| task_memory | The amount of memory (in MiB) used by the task. If using Fargate launch type `task_memory` must match supported cpu value (https://docs.aws.amazon.com/AmazonECS/latest/developerguide/task_definition_parameters.html#task_size) | number | `512` | no | +| volumes | Task volume definitions as list of configuration objects | object | `` | no | | vpc_id | The VPC ID where resources are created | string | - | yes | ## Outputs | Name | Description | |------|-------------| -| ecs_exec_role_policy_id | The ECS service role policy ID, in the form of role_name:role_policy_name | +| ecs_exec_role_policy_id | The ECS service role policy ID, in the form of `role_name:role_policy_name` | | ecs_exec_role_policy_name | ECS service role name | | service_name | ECS Service name | | service_role_arn | ECS Service role ARN | diff --git a/README.yaml b/README.yaml index f5248d3..bc62f08 100644 --- a/README.yaml +++ b/README.yaml @@ -18,9 +18,9 @@ github_repo: cloudposse/terraform-aws-ecs-alb-service-task # Badges to display badges: - - name: "Build Status" - image: "https://travis-ci.org/cloudposse/terraform-aws-ecs-alb-service-task.svg?branch=master" - url: "https://travis-ci.org/cloudposse/terraform-aws-ecs-alb-service-task" + - name: "Codefresh Build Status" + image: "https://g.codefresh.io/api/badges/pipeline/cloudposse/terraform-modules%2Fterraform-aws-ecs-alb-service-task?type=cf-1" + url: "https://g.codefresh.io/public/accounts/cloudposse/pipelines/5db352c10c7c5a56af1de612" - name: "Latest Release" image: "https://img.shields.io/github/release/cloudposse/terraform-aws-ecs-alb-service-task.svg" url: "https://github.com/cloudposse/terraform-aws-ecs-alb-service-task/releases/latest" @@ -64,68 +64,98 @@ description: |- # How to use this project usage: |- - For a complete example, see [examples/complete](examples/complete) + For a complete example, see [examples/complete](examples/complete). + + For automated test of the complete example using `bats` and `Terratest`, see [test](test). ```hcl - module "label" { - source = "git::https://github.com/cloudposse/terraform-terraform-label.git?ref=master" - namespace = "eg" - stage = "staging" - name = "app" - } - - module "container_definition" { - source = "git::https://github.com/cloudposse/terraform-aws-ecs-container-definition.git?ref=master" - container_name = "app" - container_image = "cloudposse/geodesic:latest" - - environment = [ - { - name = "string_var" - value = "I am a string" - }, - { - name = "true_boolean_var" - value = true - }, - { - name = "false_boolean_var" - value = false - }, - { - name = "integer_var" - value = 42 - }, - ] - - port_mappings = [ - { - containerPort = 8080 - hostPort = 80 - protocol = "tcp" - }, - { - containerPort = 8081 - hostPort = 443 - protocol = "udp" - }, - ] - } - - module "alb_service_task" { - source = "git::https://github.com/cloudposse/terraform-aws-ecs-alb-service-task.git?ref=master" - namespace = "eg" - stage = "staging" - name = "app" - alb_target_group_arn = "xxxxxxx" - container_definition_json = "${module.container_definition.json}" - container_name = "${module.label.id}" - ecs_cluster_arn = "xxxxxxx" - launch_type = "FARGATE" - vpc_id = "xxxxxxx" - security_group_ids = ["xxxxx", "yyyyy"] - private_subnet_ids = ["xxxxx", "yyyyy", "zzzzz"] - } + provider "aws" { + region = var.region + } + + module "label" { + source = "git::https://github.com/cloudposse/terraform-null-label.git?ref=tags/0.15.0" + namespace = var.namespace + name = var.name + stage = var.stage + delimiter = var.delimiter + attributes = var.attributes + tags = var.tags + } + + module "vpc" { + source = "git::https://github.com/cloudposse/terraform-aws-vpc.git?ref=tags/0.8.1" + namespace = var.namespace + stage = var.stage + name = var.name + delimiter = var.delimiter + attributes = var.attributes + cidr_block = var.vpc_cidr_block + tags = var.tags + } + + module "subnets" { + source = "git::https://github.com/cloudposse/terraform-aws-dynamic-subnets.git?ref=tags/0.16.1" + availability_zones = var.availability_zones + namespace = var.namespace + stage = var.stage + name = var.name + attributes = var.attributes + delimiter = var.delimiter + vpc_id = module.vpc.vpc_id + igw_id = module.vpc.igw_id + cidr_block = module.vpc.vpc_cidr_block + nat_gateway_enabled = true + nat_instance_enabled = false + tags = var.tags + } + + resource "aws_ecs_cluster" "default" { + name = module.label.id + tags = module.label.tags + } + + module "container_definition" { + source = "git::https://github.com/cloudposse/terraform-aws-ecs-container-definition.git?ref=tags/0.21.0" + container_name = var.container_name + container_image = var.container_image + container_memory = var.container_memory + container_memory_reservation = var.container_memory_reservation + container_cpu = var.container_cpu + essential = var.container_essential + readonly_root_filesystem = var.container_readonly_root_filesystem + environment = var.container_environment + port_mappings = var.container_port_mappings + log_configuration = var.container_log_configuration + } + + module "ecs_alb_service_task" { + source = "git::https://github.com/cloudposse/terraform-aws-ecs-alb-service-task.git?ref=master" + namespace = var.namespace + stage = var.stage + name = var.name + attributes = var.attributes + delimiter = var.delimiter + alb_security_group = module.vpc.vpc_default_security_group_id + container_definition_json = module.container_definition.json + ecs_cluster_arn = aws_ecs_cluster.default.arn + launch_type = var.ecs_launch_type + vpc_id = module.vpc.vpc_id + security_group_ids = [module.vpc.vpc_default_security_group_id] + subnet_ids = module.subnets.public_subnet_ids + tags = var.tags + ignore_changes_task_definition = var.ignore_changes_task_definition + network_mode = var.network_mode + assign_public_ip = var.assign_public_ip + propagate_tags = var.propagate_tags + health_check_grace_period_seconds = var.health_check_grace_period_seconds + deployment_minimum_healthy_percent = var.deployment_minimum_healthy_percent + deployment_maximum_percent = var.deployment_maximum_percent + deployment_controller_type = var.deployment_controller_type + desired_count = var.desired_count + task_memory = var.task_memory + task_cpu = var.task_cpu + } ``` The `container_image` in the `container_definition` module is the Docker image used to start a container. diff --git a/codefresh/test.yml b/codefresh/test.yml new file mode 100644 index 0000000..69fab42 --- /dev/null +++ b/codefresh/test.yml @@ -0,0 +1,77 @@ +version: '1.0' + +stages: + - Prepare + - Test + +steps: + wait: + title: Wait + stage: Prepare + image: codefresh/cli:latest + commands: + - codefresh get builds --pipeline=${{CF_REPO_NAME}} --status running --limit 1000 -o json | jq --arg id ${{CF_BUILD_ID}} -ser 'flatten|.[-1].id==$id' + retry: + maxAttempts: 10 + delay: 20 + exponentialFactor: 1.1 + + main_clone: + title: "Clone repository" + type: git-clone + stage: Prepare + description: "Initialize" + repo: ${{CF_REPO_OWNER}}/${{CF_REPO_NAME}} + git: CF-default + revision: ${{CF_REVISION}} + + clean_init: + title: Prepare build-harness and test-harness + image: ${{TEST_IMAGE}} + stage: Prepare + commands: + - cf_export PATH="/usr/local/terraform/0.12/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" + - make init + - git -C build-harness checkout master + - make -C test/ clean init TEST_HARNESS_BRANCH=master + - make -C test/src clean init + - find . -type d -name '.terraform' | xargs rm -rf + - find . -type f -name 'terraform.tfstate*' -exec rm -f {} \; + + test: + type: "parallel" + title: "Run tests" + description: "Run all tests in parallel" + stage: Test + steps: + test_readme_lint: + title: "Test README.md updated" + stage: "Test" + image: ${{TEST_IMAGE}} + description: Test "readme/lint" + commands: + - make readme/lint + + test_module: + title: Test module with bats + image: ${{TEST_IMAGE}} + stage: Test + commands: + - make -C test/ module + + test_examples_complete: + title: Test "examples/complete" with bats + image: ${{TEST_IMAGE}} + stage: Test + commands: + - make -C test/ examples/complete + + test_examples_complete_terratest: + title: Test "examples/complete" with terratest + image: ${{TEST_IMAGE}} + stage: Test + commands: + - unset AWS_PROFILE + - unset AWS_DEFAULT_PROFILE + - unset AWS_MFA_PROFILE + - make -C test/src diff --git a/docs/terraform.md b/docs/terraform.md index 6b2f899..facb8f8 100644 --- a/docs/terraform.md +++ b/docs/terraform.md @@ -3,38 +3,39 @@ | Name | Description | Type | Default | Required | |------|-------------|:----:|:-----:|:-----:| | alb_security_group | Security group of the ALB | string | - | yes | -| assign_public_ip | Assign a public IP address to the ENI (Fargate launch type only). Valid values are true or false. Default false. | string | `false` | no | -| attributes | Additional attributes (e.g. `1`) | list | `` | no | +| assign_public_ip | Assign a public IP address to the ENI (Fargate launch type only). Valid values are `true` or `false`. Default `false` | bool | `false` | no | +| attributes | Additional attributes (_e.g._ "1") | list(string) | `` | no | | container_definition_json | The JSON of the task container definition | string | - | yes | -| container_port | The port on the container to allow via the ingress security group | string | `80` | no | -| delimiter | Delimiter to be used between `name`, `namespace`, `stage`, etc. | string | `-` | no | -| deployment_controller_type | Type of deployment controller. Valid values: `CODE_DEPLOY`, `ECS`. | string | `ECS` | no | -| deployment_maximum_percent | The upper limit of the number of tasks (as a percentage of `desired_count`) that can be running in a service during a deployment | string | `200` | no | -| deployment_minimum_healthy_percent | The lower limit (as a percentage of `desired_count`) of the number of tasks that must remain running and healthy in a service during a deployment | string | `100` | no | -| desired_count | The number of instances of the task definition to place and keep running | string | `1` | no | +| container_port | The port on the container to allow via the ingress security group | number | `80` | no | +| delimiter | Delimiter between `namespace`, `stage`, `name` and `attributes` | string | `-` | no | +| deployment_controller_type | Type of deployment controller. Valid values are `CODE_DEPLOY` and `ECS` | string | `ECS` | no | +| deployment_maximum_percent | The upper limit of the number of tasks (as a percentage of `desired_count`) that can be running in a service during a deployment | number | `200` | no | +| deployment_minimum_healthy_percent | The lower limit (as a percentage of `desired_count`) of the number of tasks that must remain running and healthy in a service during a deployment | number | `100` | no | +| desired_count | The number of instances of the task definition to place and keep running | number | `1` | no | | ecs_cluster_arn | The ARN of the ECS cluster where service will be provisioned | string | - | yes | -| ecs_load_balancers | A list of load balancer config objects for the ECS service; see `load_balancer` docs https://www.terraform.io/docs/providers/aws/r/ecs_service.html | list | `` | no | -| health_check_grace_period_seconds | Seconds to ignore failing load balancer health checks on newly instantiated tasks to prevent premature shutdown, up to 7200. Only valid for services configured to use load balancers | string | `0` | no | -| ignore_changes_task_definition | Whether to ignore changes in container definition and task definition in the ECS service | string | `true` | no | +| ecs_load_balancers | A list of load balancer config objects for the ECS service; see `load_balancer` docs https://www.terraform.io/docs/providers/aws/r/ecs_service.html | object | `` | no | +| enabled | Set to false to prevent the module from creating any resources | bool | `true` | no | +| health_check_grace_period_seconds | Seconds to ignore failing load balancer health checks on newly instantiated tasks to prevent premature shutdown, up to 7200. Only valid for services configured to use load balancers | number | `0` | no | +| ignore_changes_task_definition | Whether to ignore changes in container definition and task definition in the ECS service | bool | `true` | no | | launch_type | The launch type on which to run your service. Valid values are `EC2` and `FARGATE` | string | `FARGATE` | no | -| name | Solution name, e.g. 'app' or 'cluster' | string | - | yes | -| namespace | Namespace, which could be your organization name, e.g. 'eg' or 'cp' | string | - | yes | -| network_mode | The network mode to use for the task. This is required to be awsvpc for `FARGATE` `launch_type` | string | `awsvpc` | no | -| propagate_tags | Specifies whether to propagate the tags from the task definition or the service to the tasks. The valid values are SERVICE and TASK_DEFINITION. | string | `` | no | -| security_group_ids | Security group IDs to allow in Service `network_configuration` | list | - | yes | -| stage | Stage, e.g. 'prod', 'staging', 'dev', or 'test' | string | - | yes | -| subnet_ids | Subnet IDs | list | - | yes | -| tags | Additional tags (e.g. `map('BusinessUnit`,`XYZ`) | map | `` | no | -| task_cpu | The number of CPU units used by the task. If using `FARGATE` launch type `task_cpu` must match supported memory values (https://docs.aws.amazon.com/AmazonECS/latest/developerguide/task_definition_parameters.html#task_size) | string | `256` | no | -| task_memory | The amount of memory (in MiB) used by the task. If using Fargate launch type `task_memory` must match supported cpu value (https://docs.aws.amazon.com/AmazonECS/latest/developerguide/task_definition_parameters.html#task_size) | string | `512` | no | -| volumes | Task volume definitions as list of maps | list | `` | no | +| name | Name of the application | string | - | yes | +| namespace | Namespace (e.g. `eg` or `cp`) | string | `` | no | +| network_mode | The network mode to use for the task. This is required to be `awsvpc` for `FARGATE` `launch_type` | string | `awsvpc` | no | +| propagate_tags | Specifies whether to propagate the tags from the task definition or the service to the tasks. The valid values are SERVICE and TASK_DEFINITION | string | `null` | no | +| security_group_ids | Security group IDs to allow in Service `network_configuration` | list(string) | `` | no | +| stage | Stage (e.g. `prod`, `dev`, `staging`) | string | `` | no | +| subnet_ids | Subnet IDs | list(string) | - | yes | +| tags | Additional tags (_e.g._ { BusinessUnit : ABC }) | map(string) | `` | no | +| task_cpu | The number of CPU units used by the task. If using `FARGATE` launch type `task_cpu` must match supported memory values (https://docs.aws.amazon.com/AmazonECS/latest/developerguide/task_definition_parameters.html#task_size) | number | `256` | no | +| task_memory | The amount of memory (in MiB) used by the task. If using Fargate launch type `task_memory` must match supported cpu value (https://docs.aws.amazon.com/AmazonECS/latest/developerguide/task_definition_parameters.html#task_size) | number | `512` | no | +| volumes | Task volume definitions as list of configuration objects | object | `` | no | | vpc_id | The VPC ID where resources are created | string | - | yes | ## Outputs | Name | Description | |------|-------------| -| ecs_exec_role_policy_id | The ECS service role policy ID, in the form of role_name:role_policy_name | +| ecs_exec_role_policy_id | The ECS service role policy ID, in the form of `role_name:role_policy_name` | | ecs_exec_role_policy_name | ECS service role name | | service_name | ECS Service name | | service_role_arn | ECS Service role ARN | diff --git a/examples/complete/fixtures.us-east-2.tfvars b/examples/complete/fixtures.us-east-2.tfvars new file mode 100644 index 0000000..40bb51c --- /dev/null +++ b/examples/complete/fixtures.us-east-2.tfvars @@ -0,0 +1,79 @@ +region = "us-east-2" + +availability_zones = ["us-east-2a", "us-east-2b"] + +namespace = "eg" + +stage = "test" + +name = "ecs-alb-service-task" + +vpc_cidr_block = "172.16.0.0/16" + +ecs_launch_type = "FARGATE" + +network_mode = "awsvpc" + +ignore_changes_task_definition = true + +assign_public_ip = false + +propagate_tags = "TASK_DEFINITION" + +deployment_minimum_healthy_percent = 100 + +deployment_maximum_percent = 200 + +deployment_controller_type = "ECS" + +desired_count = 1 + +task_memory = 512 + +task_cpu = 256 + +container_name = "geodesic" + +container_image = "cloudposse/geodesic" + +container_memory = 256 + +container_memory_reservation = 128 + +container_cpu = 256 + +container_essential = true + +container_readonly_root_filesystem = false + +container_environment = [ + { + name = "string_var" + value = "I am a string" + }, + { + name = "true_boolean_var" + value = true + }, + { + name = "false_boolean_var" + value = false + }, + { + name = "integer_var" + value = 42 + } +] + +container_port_mappings = [ + { + containerPort = 80 + hostPort = 80 + protocol = "tcp" + }, + { + containerPort = 443 + hostPort = 443 + protocol = "udp" + } +] diff --git a/examples/complete/main.tf b/examples/complete/main.tf index f2842b4..2cbbcaa 100644 --- a/examples/complete/main.tf +++ b/examples/complete/main.tf @@ -1,125 +1,85 @@ provider "aws" { - version = ">= 2.22.0" + region = var.region } module "label" { - source = "git::https://github.com/cloudposse/terraform-terraform-label.git?ref=master" - namespace = "eg" - stage = "staging" - name = "app" + source = "git::https://github.com/cloudposse/terraform-null-label.git?ref=tags/0.15.0" + namespace = var.namespace + name = var.name + stage = var.stage + delimiter = var.delimiter + attributes = var.attributes + tags = var.tags } -module "container_definition" { - source = "git::https://github.com/cloudposse/terraform-aws-ecs-container-definition.git?ref=master" - container_name = "app" - container_image = "cloudposse/geodesic:latest" - - environment = [ - { - name = "string_var" - value = "I am a string" - }, - { - name = "true_boolean_var" - value = true - }, - { - name = "false_boolean_var" - value = false - }, - { - name = "integer_var" - value = 42 - }, - ] - - port_mappings = [ - { - containerPort = 8080 - hostPort = 80 - protocol = "tcp" - }, - { - containerPort = 8081 - hostPort = 443 - protocol = "udp" - }, - ] +module "vpc" { + source = "git::https://github.com/cloudposse/terraform-aws-vpc.git?ref=tags/0.8.1" + namespace = var.namespace + stage = var.stage + name = var.name + delimiter = var.delimiter + attributes = var.attributes + cidr_block = var.vpc_cidr_block + tags = var.tags } -// ECS service not tied to load balancer configs -module "alb_service_task_no_lb" { - source = "../../" - namespace = "eg" - stage = "staging" - name = "app" - alb_security_group = "xxxxxxx" - container_definition_json = "${module.container_definition.json}" - ecs_cluster_arn = "xxxxxxx" - launch_type = "FARGATE" - vpc_id = "xxxxxxx" - security_group_ids = ["xxxxx", "yyyyy"] - subnet_ids = ["xxxxx", "yyyyy", "zzzzz"] - - ignore_changes_task_definition = "true" +module "subnets" { + source = "git::https://github.com/cloudposse/terraform-aws-dynamic-subnets.git?ref=tags/0.16.1" + availability_zones = var.availability_zones + namespace = var.namespace + stage = var.stage + name = var.name + attributes = var.attributes + delimiter = var.delimiter + vpc_id = module.vpc.vpc_id + igw_id = module.vpc.igw_id + cidr_block = module.vpc.vpc_cidr_block + nat_gateway_enabled = true + nat_instance_enabled = false + tags = var.tags } -// ECS service ignoring task definition changes -module "alb_service_task_ignore" { - source = "../../" - namespace = "eg" - stage = "staging" - name = "app" - alb_security_group = "xxxxxxx" - container_definition_json = "${module.container_definition.json}" - ecs_cluster_arn = "xxxxxxx" - launch_type = "FARGATE" - vpc_id = "xxxxxxx" - security_group_ids = ["xxxxx", "yyyyy"] - subnet_ids = ["xxxxx", "yyyyy", "zzzzz"] - - ignore_changes_task_definition = "true" - - ecs_load_balancers = [ - { - target_group_arn = "xxxxxxx" - container_name = "${module.label.id}" - container_port = "80" - }, - { - target_group_arn = "yyyyy" - container_name = "${module.label.id}" - container_port = "8080" - }, - ] +resource "aws_ecs_cluster" "default" { + name = module.label.id + tags = module.label.tags } -// Default ECS service -module "alb_service_task" { - source = "../../" - namespace = "eg" - stage = "staging" - name = "app" - alb_security_group = "xxxxxxx" - container_definition_json = "${module.container_definition.json}" - ecs_cluster_arn = "xxxxxxx" - launch_type = "FARGATE" - vpc_id = "xxxxxxx" - security_group_ids = ["xxxxx", "yyyyy"] - subnet_ids = ["xxxxx", "yyyyy", "zzzzz"] - - ignore_changes_task_definition = "false" +module "container_definition" { + source = "git::https://github.com/cloudposse/terraform-aws-ecs-container-definition.git?ref=tags/0.21.0" + container_name = var.container_name + container_image = var.container_image + container_memory = var.container_memory + container_memory_reservation = var.container_memory_reservation + container_cpu = var.container_cpu + essential = var.container_essential + readonly_root_filesystem = var.container_readonly_root_filesystem + environment = var.container_environment + port_mappings = var.container_port_mappings +} - ecs_load_balancers = [ - { - target_group_arn = "xxxxxxx" - container_name = "${module.label.id}" - container_port = "80" - }, - { - target_group_arn = "yyyyy" - container_name = "${module.label.id}" - container_port = "8080" - }, - ] +module "ecs_alb_service_task" { + source = "../.." + namespace = var.namespace + stage = var.stage + name = var.name + attributes = var.attributes + delimiter = var.delimiter + alb_security_group = module.vpc.vpc_default_security_group_id + container_definition_json = module.container_definition.json + ecs_cluster_arn = aws_ecs_cluster.default.arn + launch_type = var.ecs_launch_type + vpc_id = module.vpc.vpc_id + security_group_ids = [module.vpc.vpc_default_security_group_id] + subnet_ids = module.subnets.public_subnet_ids + tags = var.tags + ignore_changes_task_definition = var.ignore_changes_task_definition + network_mode = var.network_mode + assign_public_ip = var.assign_public_ip + propagate_tags = var.propagate_tags + deployment_minimum_healthy_percent = var.deployment_minimum_healthy_percent + deployment_maximum_percent = var.deployment_maximum_percent + deployment_controller_type = var.deployment_controller_type + desired_count = var.desired_count + task_memory = var.task_memory + task_cpu = var.task_cpu } diff --git a/examples/complete/outputs.tf b/examples/complete/outputs.tf new file mode 100644 index 0000000..480cb35 --- /dev/null +++ b/examples/complete/outputs.tf @@ -0,0 +1,94 @@ +output "public_subnet_cidrs" { + value = module.subnets.public_subnet_cidrs + description = "Public subnet CIDRs" +} + +output "private_subnet_cidrs" { + value = module.subnets.private_subnet_cidrs + description = "Private subnet CIDRs" +} + +output "vpc_cidr" { + value = module.vpc.vpc_cidr_block + description = "VPC ID" +} + +output "container_definition_json" { + value = module.container_definition.json + description = "JSON encoded list of container definitions for use with other terraform resources such as aws_ecs_task_definition" +} + +output "container_definition_json_map" { + value = module.container_definition.json_map + description = "JSON encoded container definitions for use with other terraform resources such as aws_ecs_task_definition" +} + +output "ecs_cluster_id" { + value = aws_ecs_cluster.default.id + description = "ECS cluster ID" +} + +output "ecs_cluster_arn" { + value = aws_ecs_cluster.default.arn + description = "ECS cluster ARN" +} + +output "ecs_exec_role_policy_id" { + description = "The ECS service role policy ID, in the form of `role_name:role_policy_name`" + value = module.ecs_alb_service_task.ecs_exec_role_policy_id +} + +output "ecs_exec_role_policy_name" { + description = "ECS service role name" + value = module.ecs_alb_service_task.ecs_exec_role_policy_name +} + +output "service_name" { + description = "ECS Service name" + value = module.ecs_alb_service_task.service_name +} + +output "service_role_arn" { + description = "ECS Service role ARN" + value = module.ecs_alb_service_task.service_role_arn +} + +output "task_exec_role_name" { + description = "ECS Task role name" + value = module.ecs_alb_service_task.task_exec_role_name +} + +output "task_exec_role_arn" { + description = "ECS Task exec role ARN" + value = module.ecs_alb_service_task.task_exec_role_arn +} + +output "task_role_name" { + description = "ECS Task role name" + value = module.ecs_alb_service_task.task_role_name +} + +output "task_role_arn" { + description = "ECS Task role ARN" + value = module.ecs_alb_service_task.task_role_arn +} + +output "task_role_id" { + description = "ECS Task role id" + value = module.ecs_alb_service_task.task_role_id +} + +output "service_security_group_id" { + description = "Security Group ID of the ECS task" + value = module.ecs_alb_service_task.service_security_group_id +} + +output "task_definition_family" { + description = "ECS task definition family" + value = module.ecs_alb_service_task.task_definition_family +} + +output "task_definition_revision" { + description = "ECS task definition revision" + value = module.ecs_alb_service_task.task_definition_revision +} diff --git a/examples/complete/variables.tf b/examples/complete/variables.tf new file mode 100644 index 0000000..5079d5f --- /dev/null +++ b/examples/complete/variables.tf @@ -0,0 +1,155 @@ +variable "region" { + type = string + description = "AWS Region" +} + +variable "availability_zones" { + type = list(string) + description = "List of availability zones" +} + +variable "vpc_cidr_block" { + type = string + description = "VPC CIDR block" +} + +variable "ecs_launch_type" { + type = string + description = "ECS launch type" +} + +variable "namespace" { + type = string + description = "Namespace (e.g. `eg` or `cp`)" +} + +variable "stage" { + type = string + description = "Stage (e.g. `prod`, `dev`, `staging`)" +} + +variable "name" { + type = string + description = "Name of the application" +} + +variable "delimiter" { + type = string + default = "-" + description = "Delimiter between `namespace`, `stage`, `name` and `attributes`" +} + +variable "attributes" { + type = list(string) + description = "Additional attributes (_e.g._ \"1\")" + default = [] +} + +variable "tags" { + type = map(string) + description = "Additional tags (_e.g._ { BusinessUnit : ABC })" + default = {} +} + +variable "container_name" { + type = string + description = "The name of the container. Up to 255 characters ([a-z], [A-Z], [0-9], -, _ allowed)" +} + +variable "container_image" { + type = string + description = "The image used to start the container. Images in the Docker Hub registry available by default" +} + +variable "container_memory" { + type = number + description = "The amount of memory (in MiB) to allow the container to use. This is a hard limit, if the container attempts to exceed the container_memory, the container is killed. This field is optional for Fargate launch type and the total amount of container_memory of all containers in a task will need to be lower than the task memory value" +} + +variable "container_memory_reservation" { + type = number + description = "The amount of memory (in MiB) to reserve for the container. If container needs to exceed this threshold, it can do so up to the set container_memory hard limit" +} + +variable "container_port_mappings" { + type = list(object({ + containerPort = number + hostPort = number + protocol = string + })) + + description = "The port mappings to configure for the container. This is a list of maps. Each map should contain \"containerPort\", \"hostPort\", and \"protocol\", where \"protocol\" is one of \"tcp\" or \"udp\". If using containers in a task with the awsvpc or host network mode, the hostPort can either be left blank or set to the same value as the containerPort" +} + +variable "container_cpu" { + type = number + description = "The number of cpu units to reserve for the container. This is optional for tasks using Fargate launch type and the total amount of container_cpu of all containers in a task will need to be lower than the task-level cpu value" +} + +variable "container_essential" { + type = bool + description = "Determines whether all other containers in a task are stopped, if this container fails or stops for any reason. Due to how Terraform type casts booleans in json it is required to double quote this value" +} + +variable "container_environment" { + type = list(object({ + name = string + value = string + })) + description = "The environment variables to pass to the container. This is a list of maps" +} + +variable "container_readonly_root_filesystem" { + type = bool + description = "Determines whether a container is given read-only access to its root filesystem. Due to how Terraform type casts booleans in json it is required to double quote this value" +} + +variable "network_mode" { + type = string + description = "The network mode to use for the task. This is required to be `awsvpc` for `FARGATE` `launch_type`" +} + +variable "task_cpu" { + type = number + description = "The number of CPU units used by the task. If using `FARGATE` launch type `task_cpu` must match supported memory values (https://docs.aws.amazon.com/AmazonECS/latest/developerguide/task_definition_parameters.html#task_size)" +} + +variable "task_memory" { + type = number + description = "The amount of memory (in MiB) used by the task. If using Fargate launch type `task_memory` must match supported cpu value (https://docs.aws.amazon.com/AmazonECS/latest/developerguide/task_definition_parameters.html#task_size)" +} + +variable "desired_count" { + type = number + description = "The number of instances of the task definition to place and keep running" +} + +variable "deployment_controller_type" { + type = string + description = "Type of deployment controller. Valid values are `CODE_DEPLOY` and `ECS`" +} + +variable "deployment_maximum_percent" { + type = number + description = "The upper limit of the number of tasks (as a percentage of `desired_count`) that can be running in a service during a deployment" +} + +variable "deployment_minimum_healthy_percent" { + type = number + description = "The lower limit (as a percentage of `desired_count`) of the number of tasks that must remain running and healthy in a service during a deployment" +} + +variable "ignore_changes_task_definition" { + type = bool + description = "Whether to ignore changes in container definition and task definition in the ECS service" +} + +variable "assign_public_ip" { + type = bool + description = "Assign a public IP address to the ENI (Fargate launch type only). Valid values are `true` or `false`. Default `false`" +} + +variable "propagate_tags" { + type = string + description = "Specifies whether to propagate the tags from the task definition or the service to the tasks. The valid values are SERVICE and TASK_DEFINITION" +} diff --git a/examples/complete/versions.tf b/examples/complete/versions.tf new file mode 100644 index 0000000..9840ed7 --- /dev/null +++ b/examples/complete/versions.tf @@ -0,0 +1,10 @@ +terraform { + required_version = "~> 0.12.0" + + required_providers { + aws = "~> 2.0" + template = "~> 2.0" + null = "~> 2.0" + local = "~> 1.3" + } +} diff --git a/main.tf b/main.tf index d96bb6b..382b257 100644 --- a/main.tf +++ b/main.tf @@ -1,58 +1,71 @@ module "default_label" { - source = "git::https://github.com/cloudposse/terraform-terraform-label.git?ref=0.2.1" - attributes = "${var.attributes}" - delimiter = "${var.delimiter}" - name = "${var.name}" - namespace = "${var.namespace}" - stage = "${var.stage}" - tags = "${var.tags}" + source = "git::https://github.com/cloudposse/terraform-null-label.git?ref=tags/0.15.0" + enabled = var.enabled + attributes = var.attributes + delimiter = var.delimiter + name = var.name + namespace = var.namespace + stage = var.stage + tags = var.tags } module "task_label" { - source = "git::https://github.com/cloudposse/terraform-terraform-label.git?ref=0.2.1" - attributes = ["${compact(concat(var.attributes, list("task")))}"] - delimiter = "${var.delimiter}" - name = "${var.name}" - namespace = "${var.namespace}" - stage = "${var.stage}" - tags = "${var.tags}" + source = "git::https://github.com/cloudposse/terraform-null-label.git?ref=tags/0.15.0" + enabled = var.enabled + context = module.default_label.context + attributes = compact(concat(var.attributes, ["task"])) } module "service_label" { - source = "git::https://github.com/cloudposse/terraform-terraform-label.git?ref=0.2.1" - attributes = ["${compact(concat(var.attributes, list("service")))}"] - delimiter = "${var.delimiter}" - name = "${var.name}" - namespace = "${var.namespace}" - stage = "${var.stage}" - tags = "${var.tags}" + source = "git::https://github.com/cloudposse/terraform-null-label.git?ref=tags/0.15.0" + enabled = var.enabled + context = module.default_label.context + attributes = compact(concat(var.attributes, ["service"])) } module "exec_label" { - source = "git::https://github.com/cloudposse/terraform-terraform-label.git?ref=0.2.1" - attributes = ["${compact(concat(var.attributes, list("exec")))}"] - delimiter = "${var.delimiter}" - name = "${var.name}" - namespace = "${var.namespace}" - stage = "${var.stage}" - tags = "${var.tags}" + source = "git::https://github.com/cloudposse/terraform-null-label.git?ref=tags/0.15.0" + enabled = var.enabled + context = module.default_label.context + attributes = compact(concat(var.attributes, ["exec"])) } resource "aws_ecs_task_definition" "default" { - family = "${module.default_label.id}" - container_definitions = "${var.container_definition_json}" - requires_compatibilities = ["${var.launch_type}"] - network_mode = "${var.network_mode}" - cpu = "${var.task_cpu}" - memory = "${var.task_memory}" - execution_role_arn = "${aws_iam_role.ecs_exec.arn}" - task_role_arn = "${aws_iam_role.ecs_task.arn}" - tags = "${module.default_label.tags}" - volume = "${var.volumes}" + count = var.enabled ? 1 : 0 + family = module.default_label.id + container_definitions = var.container_definition_json + requires_compatibilities = [var.launch_type] + network_mode = var.network_mode + cpu = var.task_cpu + memory = var.task_memory + execution_role_arn = join("", aws_iam_role.ecs_exec.*.arn) + task_role_arn = join("", aws_iam_role.ecs_task.*.arn) + tags = module.default_label.tags + + dynamic "volume" { + for_each = var.volumes + content { + host_path = lookup(volume.value, "host_path", null) + name = volume.value.name + + dynamic "docker_volume_configuration" { + for_each = lookup(volume.value, "docker_volume_configuration", []) + content { + autoprovision = lookup(docker_volume_configuration.value, "autoprovision", null) + driver = lookup(docker_volume_configuration.value, "driver", null) + driver_opts = lookup(docker_volume_configuration.value, "driver_opts", null) + labels = lookup(docker_volume_configuration.value, "labels", null) + scope = lookup(docker_volume_configuration.value, "scope", null) + } + } + } + } } # IAM data "aws_iam_policy_document" "ecs_task" { + count = var.enabled ? 1 : 0 + statement { effect = "Allow" actions = ["sts:AssumeRole"] @@ -65,12 +78,15 @@ data "aws_iam_policy_document" "ecs_task" { } resource "aws_iam_role" "ecs_task" { - name = "${module.task_label.id}" - assume_role_policy = "${data.aws_iam_policy_document.ecs_task.json}" - tags = "${module.task_label.tags}" + count = var.enabled ? 1 : 0 + name = module.task_label.id + assume_role_policy = join("", data.aws_iam_policy_document.ecs_task.*.json) + tags = module.task_label.tags } data "aws_iam_policy_document" "ecs_service" { + count = var.enabled ? 1 : 0 + statement { effect = "Allow" actions = ["sts:AssumeRole"] @@ -83,12 +99,15 @@ data "aws_iam_policy_document" "ecs_service" { } resource "aws_iam_role" "ecs_service" { - name = "${module.service_label.id}" - assume_role_policy = "${data.aws_iam_policy_document.ecs_service.json}" - tags = "${module.service_label.tags}" + count = var.enabled ? 1 : 0 + name = module.service_label.id + assume_role_policy = join("", data.aws_iam_policy_document.ecs_service.*.json) + tags = module.service_label.tags } data "aws_iam_policy_document" "ecs_service_policy" { + count = var.enabled ? 1 : 0 + statement { effect = "Allow" resources = ["*"] @@ -98,19 +117,22 @@ data "aws_iam_policy_document" "ecs_service_policy" { "elasticloadbalancing:DeregisterInstancesFromLoadBalancer", "elasticloadbalancing:RegisterInstancesWithLoadBalancer", "ec2:Describe*", - "ec2:AuthorizeSecurityGroupIngress", + "ec2:AuthorizeSecurityGroupIngress" ] } } resource "aws_iam_role_policy" "ecs_service" { - name = "${module.service_label.id}" - policy = "${data.aws_iam_policy_document.ecs_service_policy.json}" - role = "${aws_iam_role.ecs_service.id}" + count = var.enabled ? 1 : 0 + name = module.service_label.id + policy = join("", data.aws_iam_policy_document.ecs_service_policy.*.json) + role = join("", aws_iam_role.ecs_service.*.id) } # IAM role that the Amazon ECS container agent and the Docker daemon can assume data "aws_iam_policy_document" "ecs_task_exec" { + count = var.enabled ? 1 : 0 + statement { actions = ["sts:AssumeRole"] @@ -122,12 +144,15 @@ data "aws_iam_policy_document" "ecs_task_exec" { } resource "aws_iam_role" "ecs_exec" { - name = "${module.exec_label.id}" - assume_role_policy = "${data.aws_iam_policy_document.ecs_task_exec.json}" - tags = "${module.exec_label.tags}" + count = var.enabled ? 1 : 0 + name = module.exec_label.id + assume_role_policy = join("", data.aws_iam_policy_document.ecs_task_exec.*.json) + tags = module.exec_label.tags } data "aws_iam_policy_document" "ecs_exec" { + count = var.enabled ? 1 : 0 + statement { effect = "Allow" resources = ["*"] @@ -138,103 +163,136 @@ data "aws_iam_policy_document" "ecs_exec" { "ecr:GetDownloadUrlForLayer", "ecr:BatchGetImage", "logs:CreateLogStream", - "logs:PutLogEvents", + "logs:PutLogEvents" ] } } resource "aws_iam_role_policy" "ecs_exec" { - name = "${module.exec_label.id}" - policy = "${data.aws_iam_policy_document.ecs_exec.json}" - role = "${aws_iam_role.ecs_exec.id}" + count = var.enabled ? 1 : 0 + name = module.exec_label.id + policy = join("", data.aws_iam_policy_document.ecs_exec.*.json) + role = join("", aws_iam_role.ecs_exec.*.id) } # Service ## Security Groups resource "aws_security_group" "ecs_service" { - vpc_id = "${var.vpc_id}" - name = "${module.service_label.id}" + count = var.enabled ? 1 : 0 + vpc_id = var.vpc_id + name = module.service_label.id description = "Allow ALL egress from ECS service" - tags = "${module.service_label.tags}" + tags = module.service_label.tags } resource "aws_security_group_rule" "allow_all_egress" { + count = var.enabled ? 1 : 0 type = "egress" from_port = 0 to_port = 0 protocol = "-1" cidr_blocks = ["0.0.0.0/0"] - security_group_id = "${aws_security_group.ecs_service.id}" + security_group_id = join("", aws_security_group.ecs_service.*.id) } resource "aws_security_group_rule" "allow_icmp_ingress" { + count = var.enabled ? 1 : 0 type = "ingress" from_port = 8 to_port = 0 protocol = "icmp" cidr_blocks = ["0.0.0.0/0"] - security_group_id = "${aws_security_group.ecs_service.id}" + security_group_id = join("", aws_security_group.ecs_service.*.id) } resource "aws_security_group_rule" "alb" { + count = var.enabled ? 1 : 0 type = "ingress" from_port = 0 - to_port = "${var.container_port}" + to_port = var.container_port protocol = "tcp" - source_security_group_id = "${var.alb_security_group}" - security_group_id = "${aws_security_group.ecs_service.id}" + source_security_group_id = var.alb_security_group + security_group_id = join("", aws_security_group.ecs_service.*.id) } resource "aws_ecs_service" "ignore_changes_task_definition" { - count = "${var.ignore_changes_task_definition == "true" ? 1: 0}" - name = "${module.default_label.id}" - task_definition = "${aws_ecs_task_definition.default.family}:${aws_ecs_task_definition.default.revision}" - desired_count = "${var.desired_count}" - deployment_maximum_percent = "${var.deployment_maximum_percent}" - deployment_minimum_healthy_percent = "${var.deployment_minimum_healthy_percent}" - health_check_grace_period_seconds = "${var.health_check_grace_period_seconds}" - launch_type = "${var.launch_type}" - load_balancer = "${var.ecs_load_balancers}" - cluster = "${var.ecs_cluster_arn}" - propagate_tags = "${var.propagate_tags}" - tags = "${module.default_label.tags}" + count = var.enabled && var.ignore_changes_task_definition ? 1 : 0 + name = module.default_label.id + task_definition = "${join("", aws_ecs_task_definition.default.*.family)}:${join("", aws_ecs_task_definition.default.*.revision)}" + desired_count = var.desired_count + deployment_maximum_percent = var.deployment_maximum_percent + deployment_minimum_healthy_percent = var.deployment_minimum_healthy_percent + health_check_grace_period_seconds = var.health_check_grace_period_seconds + launch_type = var.launch_type + + dynamic "load_balancer" { + for_each = var.ecs_load_balancers + content { + container_name = load_balancer.value.container_name + container_port = load_balancer.value.container_port + elb_name = lookup(load_balancer.value, "elb_name", null) + target_group_arn = lookup(load_balancer.value, "target_group_arn", null) + } + } + + cluster = var.ecs_cluster_arn + propagate_tags = var.propagate_tags + tags = module.default_label.tags deployment_controller { - type = "${var.deployment_controller_type}" + type = var.deployment_controller_type } - network_configuration { - security_groups = ["${var.security_group_ids}", "${aws_security_group.ecs_service.id}"] - subnets = ["${var.subnet_ids}"] - assign_public_ip = "${var.assign_public_ip}" + # https://www.terraform.io/docs/providers/aws/r/ecs_service.html#network_configuration + dynamic "network_configuration" { + for_each = var.network_mode == "awsvpc" ? ["true"] : [] + content { + security_groups = compact(concat(var.security_group_ids, aws_security_group.ecs_service.*.id)) + subnets = var.subnet_ids + assign_public_ip = var.assign_public_ip + } } lifecycle { - ignore_changes = ["task_definition"] + ignore_changes = [task_definition] } } resource "aws_ecs_service" "default" { - count = "${var.ignore_changes_task_definition == "false" ? 1: 0}" - name = "${module.default_label.id}" - task_definition = "${aws_ecs_task_definition.default.family}:${aws_ecs_task_definition.default.revision}" - desired_count = "${var.desired_count}" - deployment_maximum_percent = "${var.deployment_maximum_percent}" - deployment_minimum_healthy_percent = "${var.deployment_minimum_healthy_percent}" - health_check_grace_period_seconds = "${var.health_check_grace_period_seconds}" - launch_type = "${var.launch_type}" - load_balancer = "${var.ecs_load_balancers}" - cluster = "${var.ecs_cluster_arn}" - propagate_tags = "${var.propagate_tags}" - tags = "${module.default_label.tags}" + count = var.enabled && var.ignore_changes_task_definition == false ? 1 : 0 + name = module.default_label.id + task_definition = "${join("", aws_ecs_task_definition.default.*.family)}:${join("", aws_ecs_task_definition.default.*.revision)}" + desired_count = var.desired_count + deployment_maximum_percent = var.deployment_maximum_percent + deployment_minimum_healthy_percent = var.deployment_minimum_healthy_percent + health_check_grace_period_seconds = var.health_check_grace_period_seconds + launch_type = var.launch_type + + dynamic "load_balancer" { + for_each = var.ecs_load_balancers + content { + container_name = load_balancer.value.container_name + container_port = load_balancer.value.container_port + elb_name = lookup(load_balancer.value, "elb_name", null) + target_group_arn = lookup(load_balancer.value, "target_group_arn", null) + } + } + + cluster = var.ecs_cluster_arn + propagate_tags = var.propagate_tags + tags = module.default_label.tags deployment_controller { - type = "${var.deployment_controller_type}" + type = var.deployment_controller_type } - network_configuration { - security_groups = ["${var.security_group_ids}", "${aws_security_group.ecs_service.id}"] - subnets = ["${var.subnet_ids}"] - assign_public_ip = "${var.assign_public_ip}" + # https://www.terraform.io/docs/providers/aws/r/ecs_service.html#network_configuration + dynamic "network_configuration" { + for_each = var.network_mode == "awsvpc" ? ["true"] : [] + content { + security_groups = compact(concat(var.security_group_ids, aws_security_group.ecs_service.*.id)) + subnets = var.subnet_ids + assign_public_ip = var.assign_public_ip + } } } diff --git a/outputs.tf b/outputs.tf index 0d1ceb8..a9c9a1c 100644 --- a/outputs.tf +++ b/outputs.tf @@ -1,59 +1,59 @@ output "ecs_exec_role_policy_id" { - description = "The ECS service role policy ID, in the form of role_name:role_policy_name" - value = "${aws_iam_role_policy.ecs_exec.id}" + description = "The ECS service role policy ID, in the form of `role_name:role_policy_name`" + value = join("", aws_iam_role_policy.ecs_exec.*.id) } output "ecs_exec_role_policy_name" { description = "ECS service role name" - value = "${aws_iam_role_policy.ecs_exec.name}" + value = join("", aws_iam_role_policy.ecs_exec.*.name) } output "service_name" { description = "ECS Service name" - value = "${element(coalescelist(aws_ecs_service.default.*.name, aws_ecs_service.ignore_changes_task_definition.*.name), 0)}" + value = join("", aws_ecs_service.default.*.name) != "" ? join("", aws_ecs_service.default.*.name) : join("", aws_ecs_service.ignore_changes_task_definition.*.name) } output "service_role_arn" { description = "ECS Service role ARN" - value = "${aws_iam_role.ecs_service.arn}" + value = join("", aws_iam_role.ecs_service.*.arn) } output "task_exec_role_name" { description = "ECS Task role name" - value = "${aws_iam_role.ecs_exec.name}" + value = join("", aws_iam_role.ecs_exec.*.name) } output "task_exec_role_arn" { description = "ECS Task exec role ARN" - value = "${aws_iam_role.ecs_exec.arn}" + value = join("", aws_iam_role.ecs_exec.*.arn) } output "task_role_name" { description = "ECS Task role name" - value = "${aws_iam_role.ecs_task.name}" + value = join("", aws_iam_role.ecs_task.*.name) } output "task_role_arn" { description = "ECS Task role ARN" - value = "${aws_iam_role.ecs_task.arn}" + value = join("", aws_iam_role.ecs_task.*.arn) } output "task_role_id" { description = "ECS Task role id" - value = "${aws_iam_role.ecs_task.unique_id}" + value = join("", aws_iam_role.ecs_task.*.unique_id) } output "service_security_group_id" { description = "Security Group ID of the ECS task" - value = "${aws_security_group.ecs_service.id}" + value = join("", aws_security_group.ecs_service.*.id) } output "task_definition_family" { description = "ECS task definition family" - value = "${aws_ecs_task_definition.default.family}" + value = join("", aws_ecs_task_definition.default.*.family) } output "task_definition_revision" { description = "ECS task definition revision" - value = "${aws_ecs_task_definition.default.revision}" + value = join("", aws_ecs_task_definition.default.*.revision) } diff --git a/test/.gitignore b/test/.gitignore new file mode 100644 index 0000000..442804a --- /dev/null +++ b/test/.gitignore @@ -0,0 +1 @@ +.test-harness diff --git a/test/Makefile b/test/Makefile new file mode 100644 index 0000000..17b2fe7 --- /dev/null +++ b/test/Makefile @@ -0,0 +1,43 @@ +TEST_HARNESS ?= https://github.com/cloudposse/test-harness.git +TEST_HARNESS_BRANCH ?= master +TEST_HARNESS_PATH = $(realpath .test-harness) +BATS_ARGS ?= --tap +BATS_LOG ?= test.log + +# Define a macro to run the tests +define RUN_TESTS +@echo "Running tests in $(1)" +@cd $(1) && bats $(BATS_ARGS) $(addsuffix .bats,$(addprefix $(TEST_HARNESS_PATH)/test/terraform/,$(TESTS))) +endef + +default: all + +-include Makefile.* + +## Provision the test-harnesss +.test-harness: + [ -d $@ ] || git clone --depth=1 -b $(TEST_HARNESS_BRANCH) $(TEST_HARNESS) $@ + +## Initialize the tests +init: .test-harness + +## Install all dependencies (OS specific) +deps:: + @exit 0 + +## Clean up the test harness +clean: + [ "$(TEST_HARNESS_PATH)" == "/" ] || rm -rf $(TEST_HARNESS_PATH) + +## Run all tests +all: module examples/complete + +## Run basic sanity checks against the module itself +module: export TESTS ?= installed lint get-modules module-pinning get-plugins provider-pinning validate terraform-docs input-descriptions output-descriptions +module: deps + $(call RUN_TESTS, ../) + +## Run tests against example +examples/complete: export TESTS ?= installed lint get-modules get-plugins validate +examples/complete: deps + $(call RUN_TESTS, ../$@) diff --git a/test/Makefile.alpine b/test/Makefile.alpine new file mode 100644 index 0000000..7925b18 --- /dev/null +++ b/test/Makefile.alpine @@ -0,0 +1,5 @@ +ifneq (,$(wildcard /sbin/apk)) +## Install all dependencies for alpine +deps:: init + @apk add --update terraform-docs@cloudposse json2hcl@cloudposse +endif diff --git a/test/src/.gitignore b/test/src/.gitignore new file mode 100644 index 0000000..31b0219 --- /dev/null +++ b/test/src/.gitignore @@ -0,0 +1,2 @@ +.gopath +vendor/ diff --git a/test/src/Gopkg.lock b/test/src/Gopkg.lock new file mode 100644 index 0000000..87bb6bd --- /dev/null +++ b/test/src/Gopkg.lock @@ -0,0 +1,92 @@ +# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'. + + +[[projects]] + digest = "1:ffe9824d294da03b391f44e1ae8281281b4afc1bdaa9588c9097785e3af10cec" + name = "github.com/davecgh/go-spew" + packages = ["spew"] + pruneopts = "UT" + revision = "8991bc29aa16c548c550c7ff78260e27b9ab7c73" + version = "v1.1.1" + +[[projects]] + digest = "1:75d6042fc66aebc974cc49b0c6c7cc3b9adb5f8130fbfa0dbec0820d990afa25" + name = "github.com/gruntwork-io/terratest" + packages = [ + "modules/collections", + "modules/customerrors", + "modules/files", + "modules/logger", + "modules/retry", + "modules/shell", + "modules/ssh", + "modules/terraform", + ] + pruneopts = "UT" + revision = "892abb2c35878d0808101bbfe6559e931dc2d354" + version = "v0.16.0" + +[[projects]] + digest = "1:0028cb19b2e4c3112225cd871870f2d9cf49b9b4276531f03438a88e94be86fe" + name = "github.com/pmezard/go-difflib" + packages = ["difflib"] + pruneopts = "UT" + revision = "792786c7400a136282c1664665ae0a8db921c6c2" + version = "v1.0.0" + +[[projects]] + digest = "1:5da8ce674952566deae4dbc23d07c85caafc6cfa815b0b3e03e41979cedb8750" + name = "github.com/stretchr/testify" + packages = [ + "assert", + "require", + ] + pruneopts = "UT" + revision = "ffdc059bfe9ce6a4e144ba849dbedead332c6053" + version = "v1.3.0" + +[[projects]] + branch = "master" + digest = "1:831470c2758c8b733941144f2803a0ccad0632c5a767415b777ebd296b5f463e" + name = "golang.org/x/crypto" + packages = [ + "curve25519", + "ed25519", + "ed25519/internal/edwards25519", + "internal/chacha20", + "internal/subtle", + "poly1305", + "ssh", + "ssh/agent", + ] + pruneopts = "UT" + revision = "22d7a77e9e5f409e934ed268692e56707cd169e5" + +[[projects]] + branch = "master" + digest = "1:76ee51c3f468493aff39dbacc401e8831fbb765104cbf613b89bef01cf4bad70" + name = "golang.org/x/net" + packages = ["context"] + pruneopts = "UT" + revision = "f3200d17e092c607f615320ecaad13d87ad9a2b3" + +[[projects]] + branch = "master" + digest = "1:181f3fd33e620b958b5ab77da177cf775cdcccd7db82963607875fbd09ae995e" + name = "golang.org/x/sys" + packages = [ + "cpu", + "unix", + ] + pruneopts = "UT" + revision = "9cd6430ef91e39e1a0ec0470cf1321a33ef1b887" + +[solve-meta] + analyzer-name = "dep" + analyzer-version = 1 + input-imports = [ + "github.com/gruntwork-io/terratest/modules/terraform", + "github.com/stretchr/testify/assert", + ] + solver-name = "gps-cdcl" + solver-version = 1 diff --git a/test/src/Gopkg.toml b/test/src/Gopkg.toml new file mode 100644 index 0000000..995bac5 --- /dev/null +++ b/test/src/Gopkg.toml @@ -0,0 +1,7 @@ +[[constraint]] + name = "github.com/stretchr/testify" + version = "1.2.2" + +[prune] + go-tests = true + unused-packages = true diff --git a/test/src/Makefile b/test/src/Makefile new file mode 100644 index 0000000..6169b68 --- /dev/null +++ b/test/src/Makefile @@ -0,0 +1,50 @@ +PACKAGE = terraform-aws-ecs-alb-service-task +GOEXE ?= /usr/bin/go +GOPATH = $(CURDIR)/.gopath +GOBIN = $(GOPATH)/bin +BASE = $(GOPATH)/src/$(PACKAGE) +PATH := $(PATH):$(GOBIN) + +export TF_DATA_DIR ?= $(CURDIR)/.terraform +export TF_CLI_ARGS_init ?= -get-plugins=true +export GOPATH + +.PHONY: all +## Default target +all: test + +ifneq (,$(wildcard /sbin/apk)) +## Install go, if not installed +$(GOEXE): + apk add --update go +endif + +ifeq ($(shell uname -s),Linux) +## Install all `dep`, if not installed +$(GOBIN)/dep: + @mkdir -p $(GOBIN) + @curl https://raw.githubusercontent.com/golang/dep/master/install.sh | sh +endif + +## Prepare the GOPATH +$(BASE): $(GOEXE) + @mkdir -p $(dir $@) + @ln -sf $(CURDIR) $@ + +## Download vendor dependencies to vendor/ +$(BASE)/vendor: $(BASE) $(GOBIN)/dep + cd $(BASE) && dep ensure + +.PHONY : init +## Initialize tests +init: $(BASE)/vendor + +.PHONY : test +## Run tests +test: init + cd $(BASE) && go test -v -timeout 30m -run TestExamplesComplete + +.PHONY : clean +## Clean up files +clean: + rm -rf .gopath/ vendor/ $(TF_DATA_DIR) diff --git a/test/src/examples_complete_test.go b/test/src/examples_complete_test.go new file mode 100644 index 0000000..ab9693b --- /dev/null +++ b/test/src/examples_complete_test.go @@ -0,0 +1,106 @@ +package test + +import ( + "encoding/json" + "github.com/gruntwork-io/terratest/modules/terraform" + "github.com/stretchr/testify/assert" + "testing" +) + +// Test the Terraform module in examples/complete using Terratest. +func TestExamplesComplete(t *testing.T) { + t.Parallel() + + terraformOptions := &terraform.Options{ + // The path to where our Terraform code is located + TerraformDir: "../../examples/complete", + Upgrade: true, + // Variables to pass to our Terraform code using -var-file options + VarFiles: []string{"fixtures.us-east-2.tfvars"}, + } + + // At the end of the test, run `terraform destroy` to clean up any resources that were created + defer terraform.Destroy(t, terraformOptions) + + // This will run `terraform init` and `terraform apply` and fail the test if there are any errors + terraform.InitAndApply(t, terraformOptions) + + // Run `terraform output` to get the value of an output variable + jsonMap := terraform.OutputRequired(t, terraformOptions, "container_definition_json_map") + // Verify we're getting back the outputs we expect + var jsonObject map[string]interface{} + err := json.Unmarshal([]byte(jsonMap), &jsonObject) + assert.NoError(t, err) + assert.Equal(t, "geodesic", jsonObject["name"]) + assert.Equal(t, "cloudposse/geodesic", jsonObject["image"]) + assert.Equal(t, 256, int((jsonObject["memory"]).(float64))) + assert.Equal(t, 128, int((jsonObject["memoryReservation"]).(float64))) + assert.Equal(t, 256, int((jsonObject["cpu"]).(float64))) + assert.Equal(t, true, jsonObject["essential"]) + assert.Equal(t, false, jsonObject["readonlyRootFilesystem"]) + + // Run `terraform output` to get the value of an output variable + vpcCidr := terraform.Output(t, terraformOptions, "vpc_cidr") + // Verify we're getting back the outputs we expect + assert.Equal(t, "172.16.0.0/16", vpcCidr) + + // Run `terraform output` to get the value of an output variable + privateSubnetCidrs := terraform.OutputList(t, terraformOptions, "private_subnet_cidrs") + // Verify we're getting back the outputs we expect + assert.Equal(t, []string{"172.16.0.0/19", "172.16.32.0/19"}, privateSubnetCidrs) + + // Run `terraform output` to get the value of an output variable + publicSubnetCidrs := terraform.OutputList(t, terraformOptions, "public_subnet_cidrs") + // Verify we're getting back the outputs we expect + assert.Equal(t, []string{"172.16.96.0/19", "172.16.128.0/19"}, publicSubnetCidrs) + + // Run `terraform output` to get the value of an output variable + ecsClusterId := terraform.Output(t, terraformOptions, "ecs_cluster_id") + // Verify we're getting back the outputs we expect + assert.Equal(t, "arn:aws:ecs:us-east-2:126450723953:cluster/eg-test-ecs-alb-service-task", ecsClusterId) + + // Run `terraform output` to get the value of an output variable + ecsClusterArn := terraform.Output(t, terraformOptions, "ecs_cluster_arn") + // Verify we're getting back the outputs we expect + assert.Equal(t, "arn:aws:ecs:us-east-2:126450723953:cluster/eg-test-ecs-alb-service-task", ecsClusterArn) + + // Run `terraform output` to get the value of an output variable + ecsExecRolePolicyName := terraform.Output(t, terraformOptions, "ecs_exec_role_policy_name") + // Verify we're getting back the outputs we expect + assert.Equal(t, "eg-test-ecs-alb-service-task-exec", ecsExecRolePolicyName) + + // Run `terraform output` to get the value of an output variable + serviceName := terraform.Output(t, terraformOptions, "service_name") + // Verify we're getting back the outputs we expect + assert.Equal(t, "eg-test-ecs-alb-service-task", serviceName) + + // Run `terraform output` to get the value of an output variable + serviceRoleArn := terraform.Output(t, terraformOptions, "service_role_arn") + // Verify we're getting back the outputs we expect + assert.Equal(t, "arn:aws:iam::126450723953:role/eg-test-ecs-alb-service-task-service", serviceRoleArn) + + // Run `terraform output` to get the value of an output variable + taskDefinitionFamily := terraform.Output(t, terraformOptions, "task_definition_family") + // Verify we're getting back the outputs we expect + assert.Equal(t, "eg-test-ecs-alb-service-task", taskDefinitionFamily) + + // Run `terraform output` to get the value of an output variable + taskExecRoleName := terraform.Output(t, terraformOptions, "task_exec_role_name") + // Verify we're getting back the outputs we expect + assert.Equal(t, "eg-test-ecs-alb-service-task-exec", taskExecRoleName) + + // Run `terraform output` to get the value of an output variable + taskExecRoleArn := terraform.Output(t, terraformOptions, "task_exec_role_arn") + // Verify we're getting back the outputs we expect + assert.Equal(t, "arn:aws:iam::126450723953:role/eg-test-ecs-alb-service-task-exec", taskExecRoleArn) + + // Run `terraform output` to get the value of an output variable + taskRoleName := terraform.Output(t, terraformOptions, "task_role_name") + // Verify we're getting back the outputs we expect + assert.Equal(t, "eg-test-ecs-alb-service-task-task", taskRoleName) + + // Run `terraform output` to get the value of an output variable + taskRoleArn := terraform.Output(t, terraformOptions, "task_role_arn") + // Verify we're getting back the outputs we expect + assert.Equal(t, "arn:aws:iam::126450723953:role/eg-test-ecs-alb-service-task-task", taskRoleArn) +} diff --git a/variables.tf b/variables.tf index 0cc3588..31d4aae 100644 --- a/variables.tf +++ b/variables.tf @@ -1,145 +1,176 @@ +variable "enabled" { + type = bool + description = "Set to false to prevent the module from creating any resources" + default = true +} + variable "namespace" { - type = "string" - description = "Namespace, which could be your organization name, e.g. 'eg' or 'cp'" + type = string + description = "Namespace (e.g. `eg` or `cp`)" + default = "" } variable "stage" { - type = "string" - description = "Stage, e.g. 'prod', 'staging', 'dev', or 'test'" + type = string + description = "Stage (e.g. `prod`, `dev`, `staging`)" + default = "" } variable "name" { - type = "string" - description = "Solution name, e.g. 'app' or 'cluster'" + type = string + description = "Name of the application" } variable "delimiter" { - type = "string" + type = string default = "-" - description = "Delimiter to be used between `name`, `namespace`, `stage`, etc." + description = "Delimiter between `namespace`, `stage`, `name` and `attributes`" } variable "attributes" { - type = "list" + type = list(string) + description = "Additional attributes (_e.g._ \"1\")" default = [] - description = "Additional attributes (e.g. `1`)" } variable "tags" { - type = "map" + type = map(string) + description = "Additional tags (_e.g._ { BusinessUnit : ABC })" default = {} - description = "Additional tags (e.g. `map('BusinessUnit`,`XYZ`)" } variable "vpc_id" { - type = "string" + type = string description = "The VPC ID where resources are created" } variable "alb_security_group" { - type = "string" + type = string description = "Security group of the ALB" } variable "ecs_cluster_arn" { - type = "string" + type = string description = "The ARN of the ECS cluster where service will be provisioned" } variable "ecs_load_balancers" { - default = [] - type = "list" + type = list(object({ + container_name = string + container_port = number + elb_name = string + target_group_arn = string + })) description = "A list of load balancer config objects for the ECS service; see `load_balancer` docs https://www.terraform.io/docs/providers/aws/r/ecs_service.html" + default = [] } variable "container_definition_json" { - type = "string" + type = string description = "The JSON of the task container definition" } variable "container_port" { + type = number description = "The port on the container to allow via the ingress security group" default = 80 } variable "subnet_ids" { + type = list(string) description = "Subnet IDs" - type = "list" } variable "security_group_ids" { description = "Security group IDs to allow in Service `network_configuration`" - type = "list" + type = list(string) + default = [] } variable "launch_type" { - type = "string" + type = string description = "The launch type on which to run your service. Valid values are `EC2` and `FARGATE`" default = "FARGATE" } variable "network_mode" { - type = "string" - description = "The network mode to use for the task. This is required to be awsvpc for `FARGATE` `launch_type`" + type = string + description = "The network mode to use for the task. This is required to be `awsvpc` for `FARGATE` `launch_type`" default = "awsvpc" } variable "task_cpu" { + type = number description = "The number of CPU units used by the task. If using `FARGATE` launch type `task_cpu` must match supported memory values (https://docs.aws.amazon.com/AmazonECS/latest/developerguide/task_definition_parameters.html#task_size)" default = 256 } variable "task_memory" { + type = number description = "The amount of memory (in MiB) used by the task. If using Fargate launch type `task_memory` must match supported cpu value (https://docs.aws.amazon.com/AmazonECS/latest/developerguide/task_definition_parameters.html#task_size)" default = 512 } variable "desired_count" { + type = number description = "The number of instances of the task definition to place and keep running" default = 1 } variable "deployment_controller_type" { - description = "Type of deployment controller. Valid values: `CODE_DEPLOY`, `ECS`." + type = string + description = "Type of deployment controller. Valid values are `CODE_DEPLOY` and `ECS`" default = "ECS" } variable "deployment_maximum_percent" { + type = number description = "The upper limit of the number of tasks (as a percentage of `desired_count`) that can be running in a service during a deployment" default = 200 } variable "deployment_minimum_healthy_percent" { + type = number description = "The lower limit (as a percentage of `desired_count`) of the number of tasks that must remain running and healthy in a service during a deployment" default = 100 } variable "health_check_grace_period_seconds" { - type = "string" + type = number description = "Seconds to ignore failing load balancer health checks on newly instantiated tasks to prevent premature shutdown, up to 7200. Only valid for services configured to use load balancers" default = 0 } variable "volumes" { - type = "list" - description = "Task volume definitions as list of maps" + type = list(object({ + host_path = string + name = string + docker_volume_configuration = list(object({ + autoprovision = bool + driver = string + driver_opts = map(string) + labels = map(string) + scope = string + })) + })) + description = "Task volume definitions as list of configuration objects" default = [] } variable "ignore_changes_task_definition" { - type = "string" + type = bool description = "Whether to ignore changes in container definition and task definition in the ECS service" - default = "true" + default = true } variable "assign_public_ip" { - type = "string" - default = "false" - description = "Assign a public IP address to the ENI (Fargate launch type only). Valid values are true or false. Default false." + type = bool + description = "Assign a public IP address to the ENI (Fargate launch type only). Valid values are `true` or `false`. Default `false`" + default = false } variable "propagate_tags" { - type = "string" - default = "" - description = "Specifies whether to propagate the tags from the task definition or the service to the tasks. The valid values are SERVICE and TASK_DEFINITION." + type = string + description = "Specifies whether to propagate the tags from the task definition or the service to the tasks. The valid values are SERVICE and TASK_DEFINITION" + default = null } diff --git a/versions.tf b/versions.tf new file mode 100644 index 0000000..9840ed7 --- /dev/null +++ b/versions.tf @@ -0,0 +1,10 @@ +terraform { + required_version = "~> 0.12.0" + + required_providers { + aws = "~> 2.0" + template = "~> 2.0" + null = "~> 2.0" + local = "~> 1.3" + } +}