I have a terraform template that create aws ecs task. I filled a variable with a list of object like this:
` variables.tf
variable "microservices" {
description = "the microservices to implement"
type = list(object({
name = string,
port = number,
secrets = optional(list(object({
key = string,
arn = string
})))
}))
`
Then in my main.tf I have the following: ` main.tf
resource "aws_ecs_task_definition" "task_definition" {
count = length("${var.microservices}")
family = "${var.microservices[count.index].name}-${var.environment}"
requires_compatibilities = ["FARGATE"]
network_mode = "awsvpc"
cpu = 1024
memory= 2048
execution_role_arn = "arn:aws:iam::xxxxx:role/service-role/xxxx-test-service-role"
container_definitions = jsonencode([
{
name = "${var.microservices[count.index].name}"
image = "${aws_ecr_repository.microservices_ecr_repos[count.index].repository_url}"
cpu = 1
essential = true
Ulimits = [{
Name = "nofile"
SoftLimit = 65535
HardLimit = 65535
}]
//length("${var.microservices[count.index].secrets}") > 0 ?
Secrets = [{
Name = length("${var.microservices[count.index].secrets}") > 0 ? "${var.microservices[count.index].secrets[0].key}" : 0
ValueFrom = length("${var.microservices[count.index].secrets}") > 0 ? "${var.microservices[count.index].secrets[0].arn}" : 0
//Name = "${var.microservices[count.index].secrets[0].key}"
//ValueFrom = "${var.microservices[count.index].secrets[0].arn}"
`
I don't understand how can I create Secrets parsing the variables. The secrets can be optional (it could exist or not). I should need a sort of for_each only in Secrets section in order to check if secret exist in input and then fill this filed.
An example of inputs is the following: `
microservices = [
{
"name" = "api",
"port" = 3000,
"secrets" = [{ "key" = "test123", "arn" = "0123"},{ "key" = "testXXX", "arn" = "1010"}] },
{
"name" = "web",
"port" = 3000
"secrets" = [{ "key" = "test456", "arn" = "4567"}]
}]
`
Anyone approach this kind of issue/configuration? What I would like to achieve is to create a task definition in aws ecs with secrets field (or empty secrets section) based on microservices input.
I tested a different data structure like here: flatten object made of nested list in terraform But in this scenario I was able to create a new data structure but when I create the resource (e.g.) aws_ecs_task_definition with a For_each it replicate some configuration like ecs tasks with the same name:
`
locals {
microservices_and_secrets = merge([
for ecs_taks, group in var.microservices:
{
for secrets_key, secret in group["secrets"]:
"${ecs_taks}-${secrets_key}" => {
name = group["name"]
port = group["port"]
secret = secret
}
}
]...)
}
`
`
resource "aws_ecs_task_definition" "task_definition" {
for_each = local.microservices_and_secrets
family = "${each.value.name}-${var.environment}" <-- ISSUE with creation because it replicates the ecs task microservice name due to foreach
requires_compatibilities = ["FARGATE"]
network_mode = "awsvpc"
cpu = 1024
memory= 2048
`
The problem is also that with this solution I can't have a microservice without any secret. e.g. the issue is the following:
`
microservices = [
{
"name" = "api",
"port" = 3000,
"secrets" = [{ "key" = "test123", "arn" = "0123"},{ "key" = "testXXX", "arn" = "1010"}] },
{
"name" = "web",
"port" = 3000
"secrets" = [{ "key" = "test456", "arn" = "4567"}]
},
{
"name" = "ciaotask",
"port" = 3000
}
]
`
`
Error: Iteration over null value
│
│ on main-aws-ecs.tf line 153, in locals:
│ 152: {
│ 153: for secrets_key, secret in group["secrets"]:
│ 154: "${ecs_taks}-${secrets_key}" => {
│ 155: name = group["name"]
│ 156: port = group["port"]
│ 157: secret = secret
│ 158: }
│ 159: }
│ ├────────────────
│ │ group["secrets"] is null
│
│ A null value cannot be used as the collection in a 'for' expression.
`
Anyone could help how can I manage the ecs task creation based on microservice input posted above? The question is, how can I create one aws_ecs_task_definition for each microservice present into microservices variable and it can have zero to n Secrets, starting from microservices variable list of objects.
I solved the issue. I started from this guide https://codeburst.io/how-to-securely-use-aws-secrets-manager-to-inject-secrets-into-ecs-using-infrastructure-as-code-ff2b39b420b6 then I created a template file like this:
`container_definitions.json.tpl
[{
"name" : "${name}",
"image": "${image}",
"cpu" : 1,
"essential" : true,
"Ulimits" : [{
"Name" : "nofile",
"SoftLimit" : 65535,
"HardLimit" : 65535
}],
"Secrets" : ${secrets},
"Environment" : ${environment},
"LogConfiguration" : {
"LogDriver" : "awslogs",
"Options" : {
"awslogs-group" : "${awslogs-group}",
"awslogs-region" : "${aws_region}",
"awslogs-stream-prefix" : "ecs"
}
},
"portMappings" : [
{
"containerPort" : 3000,
"hostPort" : 3000
}
]
}]
`
in my main.tf instead I created the resources in this way: `
*/
data "template_file" "container_definitions" {
count = length("${var.microservices}")
template = file("${path.module}/template_dir/container_definitions.json.tpl")
vars = {
aws_region = "${var.aws_region}"
cpu = 1
image = "${aws_ecr_repository.microservices_ecr_repos[count.index].repository_url}"
name = "${var.microservices[count.index].name}"
awslogs-group = "${aws_cloudwatch_log_group.cloudwatch_log_groups[count.index].id}"
environment = jsonencode("${var.microservices[count.index].environment}")
secrets = jsonencode("${var.microservices[count.index].secrets}")
}
}
/*
AWS ECS Task definition
*/
resource "aws_ecs_task_definition" "task_definition" {
count = length("${var.microservices}")
family = "${var.microservices[count.index].name}-${var.environment}"
requires_compatibilities = ["FARGATE"]
network_mode = "awsvpc"
cpu = "${var.microservices[count.index].cpu}"
memory= "${var.microservices[count.index].memory}"
execution_role_arn = "${aws_iam_role.task_execution_roles[count.index].arn}"
task_role_arn = "${aws_iam_role.task_execution_roles[count.index].arn}"
container_definitions = "${data.template_file.container_definitions[count.index].rendered}" //file("./containers_file/api.json")
}
`
In this way I was able to create a task definition in aws ecs with 0..n secrets and 0..n environment variables based on this (e.g.) input. `
microservices = [
{
"name" = "api",
"port" = 3000,
"cpu" = 1024,
"memory" = 2048,
"secrets" = [{ "name" = "test123", "valuefrom" = "0123"},{ "name" = "testXXX", "valuefrom" = "1010"}] },
{
"name" = "web",
"port" = 3000,
"cpu" = 1024,
"memory" = 2048,
"secrets" = [{ "name" = "test456", "valuefrom" = "4567"}],
"environment" = [{ "name" = "weenv", "value" = "emi_is_ok" },{ "name" = "weenv123", "value" = "emi_is_ok123" } ]
},
{
"name" = "ciaotask",
"port" = 3000
"cpu" = 1024,
"memory" = 2048
}
]
`
I hope this could help someone else that ran in the same issue.