Hi,
Trying to create a module for our company to use ECS Fargate and pass in a task definition where each service may have a different set of containers and each container has a different set of configs.
Is there a way to be able to pass something like that in without needing to jsonencode
it on the way in and then use jsondecode
in the module again.
Doing this results in the plan being (known after apply)
.
An example would be:
locals {
container_definitions = [
{
name = "nginx"
image = "nginx"
portMappings = [
{
containerPort = 80
hostPort = 80
protocol = "tcp"
}
]
healthCheck = {
command = [
"CMD-SHELL",
"curl -f http://localhost || exit 1",
]
interval = 30
retries = 3
timeout = 5
}
},
{
name = "php-fpm"
image = "php:8-fpm-alpine"
environmentFiles = [
{
type = "s3",
value = "<some-s3-arn>",
}
]
secrets = [
{
name = "<some-name>"
valueFrom = "<some-value-from>"
}
]
},
{
name = "redis"
image = "redis:alpine"
}
]
}
Thanks,
Gary
Ok, I’ve figured out a way to do it, not sure if it’s ideal though.
In my module I can do the following:
locals {
ecs_task_container_definitions = [
for container in var.ecs_task_container_definitions : {
name = container.name
image = container.image
portMappings = [
for port in container.ports : {
containerPort = port
hostPort = port
protocol = "tcp"
}
]
healthCheck = length(container.healthCheck) > 0 ? {
command = container.healthCheck
interval = 30
retries = 3
timeout = 5
} : null
environmentFiles = [
for value in container.environmentFiles : {
type = "s3"
value = value
}
]
environment = [
for name, value in container.environment : {
name = name
value = value
}
]
secrets = [
for name, valueFrom in container.secrets : {
name = name
valueFrom = valueFrom
}
]
}
]
}
variable "ecs_task_container_definitions" {
type = list(object({
name = string
image = string
ports = list(number)
healthCheck = list(string)
environmentFiles = list(string)
environment = map(string)
secrets = map(string)
}))
}
And where the module is being called from I can do this:
locals {
container_definitions = [
{
name = "nginx"
image = "nginx:1-alpine"
port = [80]
healthCheck = [
"CMD-SHELL",
"curl -f http://localhost || exit 1",
]
environmentFiles = []
environment = {
FASTCGI_PASS = "localhost"
}
secrets = {}
},
{
name = "php-fpm"
image = "php:8-fpm-alpine"
port = []
healthCheck = []
environmentFiles = [
<some-s3-arn>
]
environment = {}
secrets = {
<some-name>: "<some-value-from>"
}
},
{
name = "redis"
image= "redis:3.2.4-alpine"
port = []
healthCheck = []
environmentFiles = []
environment = {}
secrets = {}
}
]
}
Also, the (known after apply)
may have been the result of waiting for an S3 object resource to happen first.
If anyone has a better solution though, I’m open to suggestions.
Hi @garyrutland,
What you did here – defining a variable as being a collection of objects – is a pretty typical answer to this sort of problem, and so I’m not really sure what else to suggest without knowing what you found lacking about it.
If the issue here is that this is an open-ended schema defined by the remote system and that you would therefore just pass data through verbatim to that system and let it worry about validating, you could achieve that by declaring the variable as being of type any
and then just pass it through verbatim (possibly via jsonencode
) to a resource argument, although that would then not allow you to do the kind of preprocessing you seem to be doing in local.ecs_task_container_definitions
, if I’m understanding correctly the flow… if you want to interpret the data within Terraform then working with Terraform’s type system is often a good idea in order to get things validated and coerced into the shape you’re expecting.
I think what I’ve ended up with is good enough for now, perhaps if/when the optional()
functionality in objects comes out of experimental I might be able to be a bit more explicit, dynamic and forgiving at the same time.
@garyrutland
Hi there, I am in the same boat as you are, would you please mind sharing the full code on how solved/approaced this ?