This is a common question, so I’ll walk through here how to find this sort of answer yourself, and how to think about your Terraform modules in general.
If you look at the source
parameter in your terraform.tfvars
file, you’ll see that the module we use to deploy the sample apps is ecs-service-with-alb
in the infrastructure-modules
repo. This module contains the ECS container definition in a JSON file under infrastructure-modules/services/ecs-service-with-alb/container-definition/container-definition.json
, which includes settings such as CPU, memory, environment variables, etc:
[
{
"name": "${container_name}",
"image": "${image}:${version}",
"cpu": ${cpu},
"memory": ${memory},
"essential": true,
"portMappings": ${port_mappings},
"environment": ${env_vars},
"logConfiguration": {
"logDriver": "syslog",
"options": {
"tag": "${container_name} ({{.ID}})"
}
}
}
]
You could add command
to this file or any other of the ECS task definition parameters:
[
{
"name": "${container_name}",
"image": "${image}:${version}",
"cpu": ${cpu},
"memory": ${memory},
"essential": true,
"portMappings": ${port_mappings},
"environment": ${env_vars},
"logConfiguration": {
"logDriver": "syslog",
"options": {
"tag": "${container_name} ({{.ID}})"
}
},
"command": ${command}
}
]
The interpolated values (${...}
) in container-definition.json
are dynamically filled in from the Terraform code in infrastructure-modules/services/ecs-service-with-alb/main.tf
:
data "template_file" "ecs_task_container_definitions" {
template = "${file("${path.module}/container-definition/container-definition.json")}"
vars {
container_name = "${var.service_name}"
image = "${var.image}"
version = "${var.version}"
cpu = "${var.cpu}"
memory = "${var.memory}"
port_mappings = "[${join(",", data.template_file.port_mappings.*.rendered)}]"
env_vars = "[${join(",", data.template_file.all_env_vars.*.rendered)}]"
}
}
To fill in the command
dynamically, you’d probably want to add an input variable called command
:
variable "command" {
description = "Custom command to run in the Docker container"
type = "list"
}
And add it to the list of interpolated variables in ecs_task_container_definitions
:
data "template_file" "ecs_task_container_definitions" {
template = "${file("${path.module}/container-definition/container-definition.json")}"
vars {
container_name = "${var.service_name}"
image = "${var.image}"
version = "${var.version}"
cpu = "${var.cpu}"
memory = "${var.memory}"
port_mappings = "[${join(",", data.template_file.port_mappings.*.rendered)}]"
env_vars = "[${join(",", data.template_file.all_env_vars.*.rendered)}]"
command = "${jsonencode(var.command)}"
}
}
Now you can specify this command
value in terraform.tfvars
:
command = ["node", "/app/server.js", "--foo", "--bar"]
As for environment variables, you can see in the ecs_task_container_definitions
that env_vars
is filled in from data.template_file.all_env_vars
, which in turn looks up values from module.all_env_vars
:
module "all_env_vars" {
source = "git::git@github.com:gruntwork-io/package-terraform-utilities.git//modules/intermediate-variable?ref=v0.0.1"
map_value = "${merge(module.default_env_vars.map_value, var.extra_env_vars)}"
}
This combines two maps: one with a bunch of default environment variables, and one from an input variable called extra_env_vars
. You can set this input variable in your terraform.tfvars
file to provide custom environment variables:
command = ["node", "/app/server.js", "--foo", "--bar"]
extra_env_vars = {
foo = "bar"
}
If the command
and extra_env_vars
are different in each environment (dev, stage, prod), this approach should be all you need.
However, note that ecs-service-with-alb
is a fairly “generic” module. If certain services need custom settings in all environments, instead of copying those settings into each terraform.tfvars
file, a common pattern is to create wrappers for the ecs-serviec-with-alb
module that are customized to specific services.
For example, if you needed to set those environment variables and the custom command in all environments, then you could create a my-app
module in the infrastructure-modules
repo, have it “wrap” the generic ecs-service-with-alb module
and add your custom settings.
For example, you could add infrastructure-modules/services/my-app/main.tf
with the following rough structure:
module "service" {
# Use the ecs-service-with-alb module under the hood
source = "../ecs-service-with-alb"
# Pass through most params...
aws_account_id = "${var.aws_account_id}"
aws_region = "${var.aws_region}"
# You could enforce a naming convention for this service
service_name = "my-app-${var.vpc_name}"
# Set the command you need for this service
command = ["node", "/app/server.js", "--foo", "--bar"]
# Set the env vars you need for this service
extra_env_vars = {
foo = "bar"
}
# ... (other params omitted) ...
}
And now, in your terraform.tfvars
file, instead of pointing source
to ecs-service-with-alb
, you should point it to your custom my-app
module.