如何使用 Terraform 的模块输出作为另一个 Terraform 模块的输入?

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

我正在编写 Terraform 来部署 S3 存储桶和 lambda。需要 S3 存储桶,因为这是存储 zip 对象以准备部署到 lambda 的地方。

我的顶层有两个模块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 模块:

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 模块:

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"
}

当我尝试验证我的地形时,出现以下错误:

╷
│ 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?
╵

我已经遵循了关于输出和模块组合的文档,但我不确定我做错了什么。感谢所有帮助。

您的 module "s3" 块已 the count meta-argument and so the documentation under Referring to instances 适用于此处。

具体来说,module.s3 是一个对象列表,而不仅仅是一个对象(如错误消息中所提示的那样),因此当您引用它时,您需要指定该列表中的哪些元素想参考。

在您的情况下,您只有零个或一个模块实例,因此 module.s3 将是一个包含零个或一个元素的列表。因此,您需要向 Terraform 解释如果 module.s3 的元素为零但 module.lambdas.

的一个实例会发生什么

如果在没有桶时 module "lambdas" 中的 bucket_id 变量可以为空,一种写法是使用 the one function 将列表转换为如果它有一个元素,则为单个值;如果没有元素,则为 null

  bucket_id = one(module.s3.aws_s3_bucket[*].lambda_bucket.id)

此表达式首先使用 the splat operator [*] 将对象列表转换为仅包含 id 值的列表,然后使用 one 转换为单个字符串或为空。

如果您需要 bucket_id 在没有 module.s3 实例的情况下有一些其他值,那么您需要在此处指定一些其他逻辑来描述如何填充此变量在这种情况下。具体做什么取决于您需要的翻译规则;您可以在此处使用任何 Terraform 语言表达式或函数做出动态决策。