Skip to content

Commit

Permalink
Feat: lambda@edge submodule (#204)
Browse files Browse the repository at this point in the history
Co-authored-by: Andriy Knysh <[email protected]>
Co-authored-by: cloudpossebot <[email protected]>

* Add submodule for creating Lambda@Edge functions
* Add test for Lambda@Edge submodule
* Ensure all tests run in parallel.
* Fix `versions.tf` in `examples/complete` according to TFLint
  • Loading branch information
korenyoni authored Dec 28, 2021
1 parent 64bd6d9 commit cdf1d5c
Show file tree
Hide file tree
Showing 18 changed files with 863 additions and 15 deletions.
71 changes: 71 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -342,6 +342,77 @@ If there are warnings around the outputs when destroying using this module.
Then you can use this method for supressing the superfluous errors.
`TF_WARN_OUTPUT_ERRORS=1 terraform destroy`

#### Lambda@Edge

This module also features a Lambda@Edge submodule. Its `lambda_function_association` output is meant to feed directly into the variable of the same name in the parent module.

```hcl
provider "aws" {
region = var.region
}
provider "aws" {
region = "us-east-1"
alias = "us-east-1"
}
module "lambda_at_edge" {
source = "cloudposse/cloudfront-s3-cdn/aws//modules/lambda@edge"
# Cloud Posse recommends pinning every module to a specific version
# version = "x.x.x"
functions = {
origin_request = {
source = [{
content = <<-EOT
'use strict';
exports.handler = (event, context, callback) => {
//Get contents of response
const response = event.Records[0].cf.response;
const headers = response.headers;
//Set new headers
headers['strict-transport-security'] = [{key: 'Strict-Transport-Security', value: 'max-age=63072000; includeSubdomains; preload'}];
headers['content-security-policy'] = [{key: 'Content-Security-Policy', value: "default-src 'none'; img-src 'self'; script-src 'self'; style-src 'self'; object-src 'none'"}];
headers['x-content-type-options'] = [{key: 'X-Content-Type-Options', value: 'nosniff'}];
headers['x-frame-options'] = [{key: 'X-Frame-Options', value: 'DENY'}];
headers['x-xss-protection'] = [{key: 'X-XSS-Protection', value: '1; mode=block'}];
headers['referrer-policy'] = [{key: 'Referrer-Policy', value: 'same-origin'}];
//Return modified response
callback(null, response);
};
EOT
filename = "index.js"
}]
runtime = "nodejs12.x"
handler = "index.handler"
event_type = "origin-response"
include_body = false
}
}
# An AWS Provider configured for us-east-1 must be passed to the module, as Lambda@Edge functions must exist in us-east-1
providers = {
aws = aws.us-east-1
}
context = module.this.context
}
module "cdn" {
source = "cloudposse/cloudfront-s3-cdn/aws"
# Cloud Posse recommends pinning every module to a specific version
# version = "x.x.x"
...
lambda_function_association = module.lambda_at_edge.lambda_function_association
}
```




Expand Down
71 changes: 71 additions & 0 deletions README.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -307,6 +307,77 @@ usage: |-
Then you can use this method for supressing the superfluous errors.
`TF_WARN_OUTPUT_ERRORS=1 terraform destroy`
#### Lambda@Edge
This module also features a Lambda@Edge submodule. Its `lambda_function_association` output is meant to feed directly into the variable of the same name in the parent module.
```hcl
provider "aws" {
region = var.region
}
provider "aws" {
region = "us-east-1"
alias = "us-east-1"
}
module "lambda_at_edge" {
source = "cloudposse/cloudfront-s3-cdn/aws//modules/lambda@edge"
# Cloud Posse recommends pinning every module to a specific version
# version = "x.x.x"
functions = {
origin_request = {
source = [{
content = <<-EOT
'use strict';
exports.handler = (event, context, callback) => {
//Get contents of response
const response = event.Records[0].cf.response;
const headers = response.headers;
//Set new headers
headers['strict-transport-security'] = [{key: 'Strict-Transport-Security', value: 'max-age=63072000; includeSubdomains; preload'}];
headers['content-security-policy'] = [{key: 'Content-Security-Policy', value: "default-src 'none'; img-src 'self'; script-src 'self'; style-src 'self'; object-src 'none'"}];
headers['x-content-type-options'] = [{key: 'X-Content-Type-Options', value: 'nosniff'}];
headers['x-frame-options'] = [{key: 'X-Frame-Options', value: 'DENY'}];
headers['x-xss-protection'] = [{key: 'X-XSS-Protection', value: '1; mode=block'}];
headers['referrer-policy'] = [{key: 'Referrer-Policy', value: 'same-origin'}];
//Return modified response
callback(null, response);
};
EOT
filename = "index.js"
}]
runtime = "nodejs12.x"
handler = "index.handler"
event_type = "origin-response"
include_body = false
}
}
# An AWS Provider configured for us-east-1 must be passed to the module, as Lambda@Edge functions must exist in us-east-1
providers = {
aws = aws.us-east-1
}
context = module.this.context
}
module "cdn" {
source = "cloudposse/cloudfront-s3-cdn/aws"
# Cloud Posse recommends pinning every module to a specific version
# version = "x.x.x"
...
lambda_function_association = module.lambda_at_edge.lambda_function_association
}
```
include:
- "docs/targets.md"
- "docs/terraform.md"
Expand Down
80 changes: 80 additions & 0 deletions examples/complete/lambda-at-edge.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
provider "aws" {
region = "us-east-1"
alias = "us-east-1"
}

module "lambda_at_edge" {
source = "../../modules/lambda@edge"

enabled = local.enabled && var.lambda_at_edge_enabled

functions = {
# Just for the sake of a viewer-request example, inject a useless header into the request from the viewer to CF
viewer_request = {
source = [{
content = <<-EOT
'use strict';
exports.handler = (event, context, callback) => {
const { request } = event.Records[0].cf;
request.headers['useless-header'] = [
{
key: 'Useless-Header',
value: 'This header is absolutely useless.'
}
];
return callback(null, request);
};
EOT
filename = "index.js"
}]
runtime = "nodejs12.x"
handler = "index.handler"
event_type = "viewer-request"
include_body = false
},
# Add security headers to the request from CF to the origin
origin_request = {
source = [{
# https://aws.amazon.com/blogs/networking-and-content-delivery/adding-http-security-headers-using-lambdaedge-and-amazon-cloudfront/
content = <<-EOT
'use strict';
exports.handler = (event, context, callback) => {
//Get contents of response
const response = event.Records[0].cf.response;
const headers = response.headers;
//Set new headers
headers['strict-transport-security'] = [{key: 'Strict-Transport-Security', value: 'max-age=63072000; includeSubdomains; preload'}];
headers['content-security-policy'] = [{key: 'Content-Security-Policy', value: "default-src 'none'; img-src 'self'; script-src 'self'; style-src 'self'; object-src 'none'"}];
headers['x-content-type-options'] = [{key: 'X-Content-Type-Options', value: 'nosniff'}];
headers['x-frame-options'] = [{key: 'X-Frame-Options', value: 'DENY'}];
headers['x-xss-protection'] = [{key: 'X-XSS-Protection', value: '1; mode=block'}];
headers['referrer-policy'] = [{key: 'Referrer-Policy', value: 'same-origin'}];
//Return modified response
callback(null, response);
};
EOT
filename = "index.js"
}]
runtime = "nodejs12.x"
handler = "index.handler"
event_type = "origin-response"
include_body = false
}
}

# A destruction delay is always enabled due to automated tests (see variable description for more information).
destruction_delay = "20m"

providers = {
aws = aws.us-east-1
}

context = module.this.context
}
11 changes: 11 additions & 0 deletions examples/complete/lambda-at-edge.us-east-2.tfvars
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
region = "us-east-2"

namespace = "eg"

stage = "test"

name = "cf-s3-cdn-lambda" # name needs to be shortened due to s3 bucket name length restrictions

parent_zone_name = "testing.cloudposse.co"

lambda_at_edge_enabled = true
4 changes: 4 additions & 0 deletions examples/complete/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ provider "aws" {
locals {
enabled = module.this.enabled
additional_origin_groups = concat(local.additional_custom_origin_groups, local.additional_s3_origin_groups)
lambda_at_edge_enabled = local.enabled && var.lambda_at_edge_enabled
}

data "aws_partition" "current" {
Expand Down Expand Up @@ -98,6 +99,9 @@ module "cloudfront_s3_cdn" {
failover_criteria = var.origin_group_failover_criteria_status_codes
}], local.additional_origin_groups)

lambda_function_association = local.lambda_at_edge_enabled ? module.lambda_at_edge.lambda_function_association : []
forward_header_values = local.lambda_at_edge_enabled ? ["useless-header"] : []

context = module.this.context
}

Expand Down
5 changes: 5 additions & 0 deletions examples/complete/outputs.tf
Original file line number Diff line number Diff line change
Expand Up @@ -62,3 +62,8 @@ output "s3_bucket_policy" {
value = module.cloudfront_s3_cdn.s3_bucket_policy
description = "Final computed S3 bucket policy"
}

output "lambda_function_association" {
description = "The Lambda@Edge function association configuration."
value = module.lambda_at_edge.lambda_function_association
}
6 changes: 6 additions & 0 deletions examples/complete/variables.tf
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,9 @@ variable "origin_group_failover_criteria_status_codes" {
502
]
}

variable "lambda_at_edge_enabled" {
type = bool
description = "Whether or not to enable Lambda@Edge functions."
default = false
}
14 changes: 1 addition & 13 deletions examples/complete/versions.tf
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,7 @@ terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = ">= 2.0"
}
null = {
source = "hashicorp/null"
version = ">= 2.0"
}
template = {
source = "hashicorp/template"
version = ">= 2.0"
}
local = {
source = "hashicorp/local"
version = ">= 1.2"
version = ">= 3.0"
}
}
}
Loading

0 comments on commit cdf1d5c

Please sign in to comment.