Terraform 中的可选 CloudFront Lambda 函数关联

Optional CloudFront Lambda function association in Terraform

我们在 CloudFront 和 S3 上托管我们的网络应用程序。此基础架构在 Terraform 模块中配置。我们正在使用相同的模块(由 Terrag运行t 管理)将我们的 webapp 部署到我们的暂存和生产环境中。

显然,我们不希望 public 访问我们的暂存环境。因此,我们创建了一个 Lambda 函数来启用基本 HTTP 身份验证,并使用 aws_cloudfront_distribution 资源中的 lambda_function_association 来启用它。

问题是我们不希望 Lambda 在我们的生产环境中也 运行。我无法有条件地在资源上设置关联。

我也试过创建两个同名的资源并设置 count 属性 以便只有一个资源存在。

例如

# Basic Auth Guard
resource "aws_cloudfront_distribution" "default" {
  count = "${var.behind_auth_guard}"
  ...
}

# No Basic Auth Guard
resource "aws_cloudfront_distribution" "default" {
  count = "${var.behind_auth_guard ? 0 : 1}"
}

然而,当我尝试部署代码时,我得到 aws_cloudfront_distribution.default: resource repeated multiple times

有什么方法可以达到我想要的效果吗?

我考虑过的另一个选项是在两个版本上设置 Lambda,但在产品中不执行任何操作。然而,这似乎效率低下且成本高昂,因为 Lamdba 将在每次请求时被调用,并且希望尽可能避免它。

对于 Terraform v0.12.0,这将非常容易解决,因为它支持 Dynamic Nested Blocks。很遗憾,该版本不会在 2019 年第一季度之前发布。

同时,您可以按照自己提出的建议稍作修改。因为不支持重复名称,所以您只需要一个小改动。

# Basic Auth Guard
resource "aws_cloudfront_distribution" "cf_with_guard" {
  count = "${var.behind_auth_guard}"
  ...
}

# No Basic Auth Guard
resource "aws_cloudfront_distribution" "cf_no_guard" {
  count = "${var.behind_auth_guard ? 0 : 1}"
}

如果您现在想使用此资源的任何输出,您必须使用一个小 hack。例如,如果你想输出分布的 id

output "cf_id" {
  value = "${var.behind_auth_guard ? join("", aws_cloudfront_distribution.cf_with_guard.*.id) : join("", aws_cloudfront_distribution.cf_no_guard.*.id)}"
}

join() 是必需的,因为您不能引用不存在的资源。甚至不在 if 语句中。 join() 通过引用所有资源的列表来解决此问题,如果资源的 count 为 0,则该列表为空。

供将来参考的注意事项:如果发布了 v0.12.0,则不再需要上述解决方法。简单参考动态嵌套块。

正如 Joris 在上面指出的那样,动态嵌套块是可行的方法。以下是对我有用的方法:

dynamic "lambda_function_association" {
  for_each = var.ENV == "prod" ? [] : [0]
  content {
    event_type   = "viewer-request"
    lambda_arn   = "${aws_lambda_function.my_auth_lambda.qualified_arn}"
  }
}

这会为生产环境以外的所有环境启用 lambda 函数。