--- layout: docs page_title: Installing Consul on AWS ECS using Terraform description: >- Install Consul Service Mesh on AWS ECS with Terraform (Elastic Container Service). --- # Installation with Terraform This topic describes how to use the [`mesh-task`](https://registry.terraform.io/modules/hashicorp/consul-ecs/aws/latest/submodules/mesh-task) Terraform module to launch your application in AWS ECS as part of Consul service mesh. If you do not use Terraform, see the [Manual Installation](/docs/ecs/manual-installation) page to install Consul on ECS without Terraform. This topic does not include instructions for creating all AWS resources necessary to install Consul, such as a VPC or the ECS cluster. Refer to the linked guides in the [Getting Started](/docs/ecs#getting-started) section for complete, runnable examples. ## Overview This topic describes the following procedure: 1. Create Terraform configuration files for the necessary components: * [ECS task definition](#using-the-mesh-task-module): Use the `mesh-task` module to create an ECS task definition for Consul on ECS * [ECS service](#ecs-service): Use the `aws_ecs_service` resource to create an ECS service that schedules service mesh tasks to run on ECS 2. [Run Terraform](#running-terraform) to deploy the resources in AWS ## Prerequisites * You should have some familiarity with using Terraform. Refer to the [Terraform documentation](https://www.terraform.io/docs) to learn about infrastructure as code and how to get started with Terraform. * You should also be familiar with AWS ECS before following these instructions. See [What is Amazon Elastic Container Service](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/Welcome.html) for details. ## Using the Mesh Task Module To run an application in ECS with Consul service mesh, you must create an ECS task definition, which includes your application container(s) and additional sidecar containers, such as the Consul agent container and the Envoy sidecar proxy container. The [`mesh-task` module](https://registry.terraform.io/modules/hashicorp/consul-ecs/aws/latest/submodules/mesh-task) will automatically include the necessary sidecar containers. The following example shows a Terraform configuration file that creates a task definition with an application container called `example-client-app` in a file called `mesh-task.tf`: ```hcl module "my_task" { source = "hashicorp/consul-ecs/aws//modules/mesh-task" version = "" family = "my_task" container_definitions = [ { name = "example-client-app" image = "docker.io/org/my_task:v0.0.1" essential = true portMappings = [ { containerPort = 9090 hostPort = 9090 protocol = "tcp" } ] cpu = 0 mountPoints = [] volumesFrom = [] } ] port = 9090 retry_join = ["
"] } ``` The following fields are required. Refer to the [module reference documentation](https://registry.terraform.io/modules/hashicorp/consul-ecs/aws/latest/submodules/mesh-task?tab=inputs) for a complete reference. | Input Variable | Type | Description | | ----------------------- | -------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `source` | string | Must be set to the source location of the `mesh-task` module, `hashicorp/consul-ecs/aws//modules/mesh-task`. | | `version` | string | Must be set to the version of the `mesh-task` module. | | `family` | string | The [ECS task definition family](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/task_definition_parameters.html#family). The family is also used as the Consul service name by default. | | `container_definitions` | list | This is the list of [container definitions](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/task_definition_parameters.html#container_definitions) for the task definition. This is where you include your application containers. | | `port` | integer | The port that your application listens on, if any. If your application does not listen on a port, set `outbound_only = true`. | | `retry_join` | list | The is the [`retry_join`](/docs/agent/options#_retry_join) option for the Consul agent, which specifies the locations of your Consul servers. | ### Running Terraform You will need to run Terraform to create the task definition. Save the Terraform configuration for the task definition to a file, such as `mesh-task.tf`. You should place this file in a directory alongside other Terraform configuration files for your project. The `mesh-task` module requires the AWS Terraform provider. The following example shows how to include and configure the AWS provider in a file called `provider.tf`. Refer to the [AWS Terraform provider](https://registry.terraform.io/providers/hashicorp/aws/latest/docs) documentation for complete configuration details. ```hcl terraform { required_providers { aws = { source = "hashicorp/aws" version = "" } } } provider "aws" { region = "" ... } ``` Additional AWS resources for your project can be included in additional Terraform configuration files in the same directory. The following example shows a basic project directory: ```shell-session $ ls mesh-task.tf provider.tf ... ``` Terraform should be run in your project directory as follows. * Run `terraform init` first to download dependencies, such as Terraform providers * Run `terraform apply` to have Terraform create AWS resources, such as the task definition from the `mesh-task` module. Terraform automatically reads all files in the current directory that have a `.tf` file extension. Refer to the [Terraform documentation](https://www.terraform.io/docs) for more information and Terraform best practices. ## ECS Service [ECS services](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/ecs_services.html) are one of the most common ways to start tasks using a task definition. To define an ECS service, reference the `mesh-task` module's `task_definition_arn` output value in your `aws_ecs_service` resource. The following example shows how to include the service in the `mesh-task.tf` file. ```hcl module "my_task" { source = "hashicorp/consul-ecs/aws//modules/mesh-task" ... } resource "aws_ecs_service" "my_task" { name = "my_task_service" task_definition = module.my_task.task_definition_arn launch_type = "FARGATE" propagate_tags = "TASK_DEFINITION" ... } ``` This is a partial configuration to highlight some important fields. See the [`aws_ecs_service`](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/ecs_service) documentation for a complete reference. | Input Variable | Type | Description | | ----------------- | ------- | ------------------------------------------------------------------------------------------------------------------- | | `name` | string | The name of the ECS service. This is required by AWS but is not used by Consul service mesh. | | `task_definition` | string | The task definition used to start tasks. Set this to the task definition ARN returned by the `mesh-task` module. | | `launch_type` | string | The launch type. Consul on ECS supports the `FARGATE` and `EC2` launch types. | | `propagate_tags` | string | This must be set to `TASK_DEFINITION` so that tags added by `mesh-task` to the task definition are copied to tasks. | After including the ECS service in your Terraform configuration, run `terraform apply` from your project directory to create the ECS service resource. The ECS service will soon start your application in a task. The task will automatically register itself into the Consul service catalog during startup. -> **NOTE:** If your tasks run in a public subnet, they must have `assign_public_ip = true` in their [`network_configuration`](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/ecs_service#network_configuration) block so that ECS can pull the Docker images. ## Routing Now that your tasks are registered in the mesh, you're able to use the service mesh to route between them. In order to make calls through the service mesh, you must configure the sidecar proxy to listen on a different port for each upstream service your application needs to call. You then must modify your application to make requests to the sidecar proxy on that port. For example, if your application `web` makes calls to another application called `backend`, then you would first configure the `mesh-task` module's upstream(s): ```hcl module "web" { family = "web" upstreams = [ { destinationName = "backend" localBindPort = 8080 } ] } ``` | Input Variable | Type | Description | | ----------------- | ------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `destinationName` | string | The name of the upstream service, as it is registered in the Consul service catalog. | | `localBindPort` | integer | Requests to this port will be forwarded by the proxy to the upstream service. This must be an unused port, but does not need to match the upstream service port. | If you have multiple upstream services they each need to be listed here. Next, configure your application to make requests to `localhost:8080` when it wants to call the `backend` service. For example, if your service allows configuring the URL for `backend` via the `BACKEND_URL` environment variable, you would set: ```hcl module "web" { family = "web" upstreams = [ { destinationName = "backend" localBindPort = 8080 } ] container_definitions = [ { name = "web" environment = [ { name = "BACKEND_URL" value = "http://localhost:8080" } ] ... } ] ... } ``` ## Bind Address To ensure that your application only receives traffic through the service mesh, you must change the address that your application is listening on to only the loopback address (also known as `localhost`, `lo`, and `127.0.0.1`) so that only the sidecar proxy running in the same task can make requests to it. If your application is listening on all interfaces, e.g. `0.0.0.0`, then other applications can call it directly, bypassing its sidecar proxy. Changing the listening address is specific to the language and framework you're using in your application. Regardless of which language/framework you're using, it's a good practice to make the address configurable via environment variable. For example in Go, you would use: ```go s := &http.Server{ Addr: "127.0.0.1:8080", ... } log.Fatal(s.ListenAndServe()) ``` In Django you'd use: ```bash python manage.py runserver "127.0.0.1:8080" ``` ## Next Steps - Follow the [Secure Configuration](/docs/ecs/secure-configuration) to get production-ready. - Now that your applications are running in the service mesh, read about other [Service Mesh features](/docs/connect). - View the [Architecture](/docs/ecs/architecture) documentation to understand what's going on under the hood.