简体   繁体   中英

Optional CloudFront Lambda function association in Terraform

We're hosting our webapp on CloudFront and S3. This infrastructure is configured in a Terraform module. We're using the same module (managed by Terragrunt) to deploy our webapp to our staging and production environments.

Obviously, we don't want public access to our staging environment. As such, we've created a Lambda function to enabled Basic HTTP Auth and are using the lambda_function_association within the aws_cloudfront_distribution resource to enable it.

The issue is we don't want the Lambda to run on our prod environment as well. I haven't been able to conditionally set the association on the resource.

I've also tried creating two resources with the same name and setting the count property so that only of the resources exists.

eg

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

However when I try to deploy the code, I get aws_cloudfront_distribution.default: resource repeated multiple times .

Is there any way to achieve what I want?

Another option that I've considered is setting the Lambda on both versions, but having it not do anything in prod. However, this seems inefficient and costly as the Lamdba will be called on every request, and would like to avoid it if possible.

With v0.12.0 of Terraform this will be really easy to solve because it supports Dynamic Nested Blocks . Unfortunately, that version won't be released before 2019 Q1.

In the meantime, you can go with the suggestion you proposed yourself, with a minor change. You only need a small change because duplicate names are not supported.

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

If you now want to use any outputs from this resource, you have to use a small hack . Eg, if you want to output the id of the distribution:

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

The join() is needed because you cannot make a reference to a resource that does not exist. Not even in an if-statement. The join() works around this by referring to the list of all resources, which is empty if the count for the resource is 0.

Note for future reference: if v0.12.0 is released, the above workarounds are not necessary anymore. Simply refer to Dynamic Nested Blocks.

As Joris points out above dynamic nested blocks are the way to go. Here's what worked for me:

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

This turns the lambda function on for everything but the production environment.

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