I’m triying to use a map with for_each for created some lambdas, this lambdas has diferents configurations, and one is the argument "layers", I mean, that some lambdas will has 3 layers, others 2 layers, others 1 layer and finally others nothing layer, I try to test only with the lambdas that has 1 layer, but I get this error:
Error: Invalid index
│
│ on modules/lm/main.tf line 31, in resource "aws_lambda_function" "lml3":
│ 31: layers = each.value.layer1 == "null" ? null : aws_lambda_layer_version.lm[each.value.layer1].arn
│ ├────────────────
│ │ aws_lambda_layer_version.lm is object with 1 attribute "capa-openpyxl-s3fs-xlrd-fsspec"
│ │ each.value.layer1 is list of string with 1 element
│
│ The given key does not identify an element in this collection value: string required.
terraform_version": "1.4.1" provider registry.terraform.io/hashicorp/aws v4.67.0
variables.tf
### Global variables ###
variable "stack_id" {
type = string
}
variable "vpc_id" {
type = string
}
### Lambda variables ###
variable "lambda_function_l3" {
type = map(object({
memory_size = number,
runtime = string,
handler = string,
s3_key = string,
role = string
layer1 = optional(list(string)),
env_key = map(any)
}))
}
variable "layers" {
type = map(any)
}
main.tf (lambdas)
resource "aws_lambda_function" "lml3" {
for_each = var.lambda_function_l3
s3_bucket = aws_s3_bucket.lm.id
s3_key = "code/${each.value.s3_key}"
function_name = "lm-${var.stack_id}-${each.key}"
role = aws_iam_role.lm[each.value.role].arn
handler = "index.${each.value.handler}"
runtime = each.value.runtime
memory_size = each.value.memory_size
layers = each.value.layer1 == "null" ? null :aws_lambda_layer_version.lm[each.value.layer1].arn
depends_on = [ aws_lambda_layer_version.lm ]
environment {
variables = each.value.env_key
}
vpc_config {
subnet_ids = [local.subnet_private[0], local.subnet_private[5]]
security_group_ids = ["sg-0591b309a73f15dd4"]
}
}
main.tf(layers)
locals {
configuration_layer = merge([
for key, values in var.layers : {
for layer in values.filename :
"${layer}" =>
merge(values, {
layer = layer
})
}
]...)
}
resource "aws_lambda_layer_version" "lm" {
for_each = local.configuration_layer
filename = "${path.module}/layers/${each.key}.zip"
layer_name = "lm-layer-${var.stack_id}-${each.value.layer}"
compatible_runtimes = [each.value.compatible_runtimes]
compatible_architectures = [each.value.compatible_architectures]
}
terraform.tfvars
layers = {
"layers-lm-test-01" = { filename = ["capa-openpyxl-s3fs-xlrd-fsspec"], compatible_runtimes = "python3.9", compatible_architectures = "x86_64" }
}
lambda_function_l3 = {
"test-01" = { memory_size = 512, runtime = "python3.9", handler = "lambda_handler", s3_key = "test_lm.zip", role = "s3_rw", layer1 = ["capa-openpyxl-s3fs-xlrd-fsspec"], env_key = { house = "car", test2 = "apple" } }
The error you saw is correct that it isn't valid to use a list as a lookup key for a map, because in Terraform map keys are always individual strings.
If your goal is for the layers
function of aws_lambda_function
to include the ARN of each layer indicated in the layers1
list then one way to write that would be:
layers = (
each.value.layer1 != null ?
[ for k in each.value.layer1 : aws_lambda_layer_version.lm[k].arn ] :
null
)
This for
expression takes each element of each.value.layer1
and uses it to look up one instance of aws_lambda_layer_version.lm
with a matching key, and then takes the arn
attribute of that object.
This will succeed only if all of the values given in layer1
correspond with keys in var.layers
, which doesn't seem to be true in the example terraform.tfvars
file you shared, but does seem to be assumed by what you tried on your first attempt.
Note that your declaration of variable "layers"
is incorrect, because your module clearly cannot accept "any element type": we can see in the definition of local.configuration_layer
that your module requires a filename
attribute, and in your resource "aws_lambda_layer_version" "lm"
the module requires compatible_runtimes
, and compatible_architectures
attributes.
Therefore the correct declaration for that variable would be:
variable "layers" {
type = map(object({
filename = string
compatible_runtimes = string
compatible_architectures = string
}))
}
Specifying your type constraints correctly will help Terraform give you better suggestions when you make a mistake.
(Given the names I'd also expect compatible_runtimes
and compatible_architectures
to either be set(string)
or list(string)
, but I see that your module expects to be given only a single one and then wraps it in a collection itself, so I wrote a type constraint compatible with how your module is currently written.)