I am writing Terraform to deploy an S3 bucket and a lambda. The S3 bucket is needed as this is where the zip object will be stored ready to deploy to lambda.
I have two modules in my top-level main.tf:
module "s3" {
count = var.enable_s3 ? 1 : 0
source = "./modules/s3"
}
module "lambdas" {
count = var.enable_lambdas ? 1 : 0
source = "./modules/lambdas"
bucket_id = module.s3.lambda_bucket_id
}
s3 module:
main.tf
resource "aws_s3_bucket" "lambda_bucket" {
bucket = "lunchboxd-lambdas"
tags = {
Owner = "Terraform",
Description = "A bucket to hold the lambdas zip"
}
}
outputs.tf
output "lambda_bucket_id" {
value = aws_s3_bucket.lambda_bucket.id
description = "ID of the bucket holding the lambda functions."
}
lambdas module:
variables.tf
variable "bucket_id" {}
main.tf
data "archive_file" "lambda_hello_world" {
type = "zip"
source_dir = "${path.module}/hello-world"
output_path = "${path.module}/hello-world.zip"
}
resource "aws_s3_object" "lambda_hello_world" {
bucket = var.bucket_id
key = "hello-world.zip"
source = data.archive_file.lambda_hello_world.output_path
etag = filemd5(data.archive_file.lambda_hello_world.output_path)
}
resource "aws_lambda_function" "hello_world" {
function_name = "HelloWorld"
s3_bucket = var.bucket_id
s3_key = aws_s3_object.lambda_hello_world.key
runtime = "nodejs12.x"
handler = "hello.handler"
source_code_hash = data.archive_file.lambda_hello_world.output_base64sha256
role = aws_iam_role.lambda_exec.arn
}
resource "aws_cloudwatch_log_group" "hello_world" {
name = "/aws/lambda/${aws_lambda_function.hello_world.function_name}"
retention_in_days = 30
}
resource "aws_iam_role" "lambda_exec" {
name = "serverless_lambda"
assume_role_policy = jsonencode({
Version = "2012-10-17"
Statement = [{
Action = "sts:AssumeRole"
Effect = "Allow"
Sid = ""
Principal = {
Service = "lambda.amazonaws.com"
}
}
]
})
}
resource "aws_iam_role_policy_attachment" "lambda_policy" {
role = aws_iam_role.lambda_exec.name
policy_arn = "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
}
When I try to validate my terraform I am getting the following error:
╷
│ Error: Unsupported attribute
│
│ on main.tf line 36, in module "lambdas":
│ 36: bucket_id = module.s3.aws_s3_bucket.lambda_bucket.id
│ ├────────────────
│ │ module.s3 is a list of object, known only after apply
│
│ Can't access attributes on a list of objects. Did you mean to access an attribute for a specific element of the list, or across all elements of the list?
╵
I have followed the documentation on Outputs and module composition but I am unsure what I am doing wrong. All help appreciated.
CodePudding user response:
Your module "s3"
block has the count
meta-argument and so the documentation under Referring to instances applies here.
Specifically, module.s3
is a list of objects rather than just a single object (as hinted in the error message) and so when you refer to it you need to specify which of the elements of that list you want to refer to.
In your case you only have zero or one instances of the module, and so module.s3
would be a list of either zero or one elements. You'll therefore need to explain to Terraform what should happen if there are zero elements of module.s3
but one instance of module.lambdas
.
If it's acceptable for the bucket_id
variable in module "lambdas"
to be null when there is no bucket, one way to write this would be to use the one
function to convert the list into either a single value if it has one element or to null
if it has no elements:
bucket_id = one(module.s3.aws_s3_bucket[*].lambda_bucket.id)
This expression first uses the splat operator [*]
to translate from the list of objects to a list of just the id
values, and then uses one
to translate to either a single string or to a null.
If you need bucket_id
to have some other value in the case where there are no instances of module.s3
then you'll need to specify some other logic here to describe how to populate this variable in that case. Exactly what to do will depend on the translation rule you need; you can use any Terraform language expression or function to make a dynamic decision here.