英文:
Terraform : I get "Invalid index" when I use a map and for_each to try to create some lambdas
问题
我正在尝试使用for_each
创建一些lambda函数的地图,这些lambda函数具有不同的配置,其中一个参数是“layers”,这意味着某些lambda函数将具有3个层,其他lambda函数将具有2个层,其他lambda函数将具有1个层,最后一些lambda函数将不具有任何层。我尝试仅测试具有1个层的lambda函数,但我收到以下错误消息:
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
### 全局变量 ###
variable "stack_id" {
type = string
}
variable "vpc_id" {
type = string
}
### Lambda 变量 ###
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(lambda函数)
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(层)
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" } }
}
英文:
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" } }
答案1
得分: 0
错误消息显示,将列表用作映射的查找键是无效的,因为在 Terraform 中,映射键始终是单个字符串。
如果您的目标是让 aws_lambda_function
的 layers
函数包含 layers1
列表中指定的每个层的 ARN,那么可以通过以下方式编写:
layers = (
each.value.layer1 != null ?
[ for k in each.value.layer1 : aws_lambda_layer_version.lm[k].arn ] :
null
)
此 for
表达式 对 each.value.layer1
的每个元素进行迭代,并使用它来查找具有匹配键的 aws_lambda_layer_version.lm
实例,然后获取该对象的 arn
属性。
这只会成功,如果在 layer1
中给定的所有值都与 var.layers
中的键对应,但根据您分享的示例 terraform.tfvars
文件,似乎这不成立,但似乎是您首次尝试时假定的。
请注意,您的 variable "layers"
声明是不正确的,因为您的模块显然 不能 接受 "任何元素类型":我们可以在 local.configuration_layer
的定义中看到,您的模块需要一个 filename
属性,并且在您的 resource "aws_lambda_layer_version" "lm"
中,模块需要 compatible_runtimes
和 compatible_architectures
属性。
因此,该变量的正确声明应该是:
variable "layers" {
type = map(object({
filename = string
compatible_runtimes = string
compatible_architectures = string
}))
}
正确指定类型约束将有助于 Terraform 在您犯错误时提供更好的建议。
(鉴于名称,我也希望 compatible_runtimes
和 compatible_architectures
要么是 set(string)
,要么是 list(string)
,但我看到您的模块期望只提供一个,然后在模块内部将其包装到集合中,因此我编写了与您的模块当前编写方式兼容的类型约束。)
英文:
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.)
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论