简体   繁体   中英

How can I use a Terraform's modules output as an input to another Terraform module?

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.

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM