Skip to content

Commit cdf1d5c

Browse files
authored
Feat: lambda@edge submodule (#204)
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
1 parent 64bd6d9 commit cdf1d5c

File tree

18 files changed

+863
-15
lines changed

18 files changed

+863
-15
lines changed

README.md

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -342,6 +342,77 @@ If there are warnings around the outputs when destroying using this module.
342342
Then you can use this method for supressing the superfluous errors.
343343
`TF_WARN_OUTPUT_ERRORS=1 terraform destroy`
344344

345+
#### Lambda@Edge
346+
347+
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.
348+
349+
```hcl
350+
provider "aws" {
351+
region = var.region
352+
}
353+
354+
provider "aws" {
355+
region = "us-east-1"
356+
alias = "us-east-1"
357+
}
358+
359+
module "lambda_at_edge" {
360+
source = "cloudposse/cloudfront-s3-cdn/aws//modules/lambda@edge"
361+
# Cloud Posse recommends pinning every module to a specific version
362+
# version = "x.x.x"
363+
364+
functions = {
365+
origin_request = {
366+
source = [{
367+
content = <<-EOT
368+
'use strict';
369+
370+
exports.handler = (event, context, callback) => {
371+
372+
//Get contents of response
373+
const response = event.Records[0].cf.response;
374+
const headers = response.headers;
375+
376+
//Set new headers
377+
headers['strict-transport-security'] = [{key: 'Strict-Transport-Security', value: 'max-age=63072000; includeSubdomains; preload'}];
378+
headers['content-security-policy'] = [{key: 'Content-Security-Policy', value: "default-src 'none'; img-src 'self'; script-src 'self'; style-src 'self'; object-src 'none'"}];
379+
headers['x-content-type-options'] = [{key: 'X-Content-Type-Options', value: 'nosniff'}];
380+
headers['x-frame-options'] = [{key: 'X-Frame-Options', value: 'DENY'}];
381+
headers['x-xss-protection'] = [{key: 'X-XSS-Protection', value: '1; mode=block'}];
382+
headers['referrer-policy'] = [{key: 'Referrer-Policy', value: 'same-origin'}];
383+
384+
//Return modified response
385+
callback(null, response);
386+
};
387+
EOT
388+
filename = "index.js"
389+
}]
390+
runtime = "nodejs12.x"
391+
handler = "index.handler"
392+
event_type = "origin-response"
393+
include_body = false
394+
}
395+
}
396+
397+
# An AWS Provider configured for us-east-1 must be passed to the module, as Lambda@Edge functions must exist in us-east-1
398+
providers = {
399+
aws = aws.us-east-1
400+
}
401+
402+
context = module.this.context
403+
}
404+
405+
406+
module "cdn" {
407+
source = "cloudposse/cloudfront-s3-cdn/aws"
408+
# Cloud Posse recommends pinning every module to a specific version
409+
# version = "x.x.x"
410+
411+
...
412+
lambda_function_association = module.lambda_at_edge.lambda_function_association
413+
}
414+
```
415+
345416

346417

347418

README.yaml

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -307,6 +307,77 @@ usage: |-
307307
Then you can use this method for supressing the superfluous errors.
308308
`TF_WARN_OUTPUT_ERRORS=1 terraform destroy`
309309
310+
#### Lambda@Edge
311+
312+
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.
313+
314+
```hcl
315+
provider "aws" {
316+
region = var.region
317+
}
318+
319+
provider "aws" {
320+
region = "us-east-1"
321+
alias = "us-east-1"
322+
}
323+
324+
module "lambda_at_edge" {
325+
source = "cloudposse/cloudfront-s3-cdn/aws//modules/lambda@edge"
326+
# Cloud Posse recommends pinning every module to a specific version
327+
# version = "x.x.x"
328+
329+
functions = {
330+
origin_request = {
331+
source = [{
332+
content = <<-EOT
333+
'use strict';
334+
335+
exports.handler = (event, context, callback) => {
336+
337+
//Get contents of response
338+
const response = event.Records[0].cf.response;
339+
const headers = response.headers;
340+
341+
//Set new headers
342+
headers['strict-transport-security'] = [{key: 'Strict-Transport-Security', value: 'max-age=63072000; includeSubdomains; preload'}];
343+
headers['content-security-policy'] = [{key: 'Content-Security-Policy', value: "default-src 'none'; img-src 'self'; script-src 'self'; style-src 'self'; object-src 'none'"}];
344+
headers['x-content-type-options'] = [{key: 'X-Content-Type-Options', value: 'nosniff'}];
345+
headers['x-frame-options'] = [{key: 'X-Frame-Options', value: 'DENY'}];
346+
headers['x-xss-protection'] = [{key: 'X-XSS-Protection', value: '1; mode=block'}];
347+
headers['referrer-policy'] = [{key: 'Referrer-Policy', value: 'same-origin'}];
348+
349+
//Return modified response
350+
callback(null, response);
351+
};
352+
EOT
353+
filename = "index.js"
354+
}]
355+
runtime = "nodejs12.x"
356+
handler = "index.handler"
357+
event_type = "origin-response"
358+
include_body = false
359+
}
360+
}
361+
362+
# An AWS Provider configured for us-east-1 must be passed to the module, as Lambda@Edge functions must exist in us-east-1
363+
providers = {
364+
aws = aws.us-east-1
365+
}
366+
367+
context = module.this.context
368+
}
369+
370+
371+
module "cdn" {
372+
source = "cloudposse/cloudfront-s3-cdn/aws"
373+
# Cloud Posse recommends pinning every module to a specific version
374+
# version = "x.x.x"
375+
376+
...
377+
lambda_function_association = module.lambda_at_edge.lambda_function_association
378+
}
379+
```
380+
310381
include:
311382
- "docs/targets.md"
312383
- "docs/terraform.md"
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
provider "aws" {
2+
region = "us-east-1"
3+
alias = "us-east-1"
4+
}
5+
6+
module "lambda_at_edge" {
7+
source = "../../modules/lambda@edge"
8+
9+
enabled = local.enabled && var.lambda_at_edge_enabled
10+
11+
functions = {
12+
# Just for the sake of a viewer-request example, inject a useless header into the request from the viewer to CF
13+
viewer_request = {
14+
source = [{
15+
content = <<-EOT
16+
'use strict';
17+
18+
exports.handler = (event, context, callback) => {
19+
const { request } = event.Records[0].cf;
20+
21+
request.headers['useless-header'] = [
22+
{
23+
key: 'Useless-Header',
24+
value: 'This header is absolutely useless.'
25+
}
26+
];
27+
28+
return callback(null, request);
29+
};
30+
EOT
31+
filename = "index.js"
32+
}]
33+
runtime = "nodejs12.x"
34+
handler = "index.handler"
35+
event_type = "viewer-request"
36+
include_body = false
37+
},
38+
# Add security headers to the request from CF to the origin
39+
origin_request = {
40+
source = [{
41+
# https://aws.amazon.com/blogs/networking-and-content-delivery/adding-http-security-headers-using-lambdaedge-and-amazon-cloudfront/
42+
content = <<-EOT
43+
'use strict';
44+
45+
exports.handler = (event, context, callback) => {
46+
47+
//Get contents of response
48+
const response = event.Records[0].cf.response;
49+
const headers = response.headers;
50+
51+
//Set new headers
52+
headers['strict-transport-security'] = [{key: 'Strict-Transport-Security', value: 'max-age=63072000; includeSubdomains; preload'}];
53+
headers['content-security-policy'] = [{key: 'Content-Security-Policy', value: "default-src 'none'; img-src 'self'; script-src 'self'; style-src 'self'; object-src 'none'"}];
54+
headers['x-content-type-options'] = [{key: 'X-Content-Type-Options', value: 'nosniff'}];
55+
headers['x-frame-options'] = [{key: 'X-Frame-Options', value: 'DENY'}];
56+
headers['x-xss-protection'] = [{key: 'X-XSS-Protection', value: '1; mode=block'}];
57+
headers['referrer-policy'] = [{key: 'Referrer-Policy', value: 'same-origin'}];
58+
59+
//Return modified response
60+
callback(null, response);
61+
};
62+
EOT
63+
filename = "index.js"
64+
}]
65+
runtime = "nodejs12.x"
66+
handler = "index.handler"
67+
event_type = "origin-response"
68+
include_body = false
69+
}
70+
}
71+
72+
# A destruction delay is always enabled due to automated tests (see variable description for more information).
73+
destruction_delay = "20m"
74+
75+
providers = {
76+
aws = aws.us-east-1
77+
}
78+
79+
context = module.this.context
80+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
region = "us-east-2"
2+
3+
namespace = "eg"
4+
5+
stage = "test"
6+
7+
name = "cf-s3-cdn-lambda" # name needs to be shortened due to s3 bucket name length restrictions
8+
9+
parent_zone_name = "testing.cloudposse.co"
10+
11+
lambda_at_edge_enabled = true

examples/complete/main.tf

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ provider "aws" {
55
locals {
66
enabled = module.this.enabled
77
additional_origin_groups = concat(local.additional_custom_origin_groups, local.additional_s3_origin_groups)
8+
lambda_at_edge_enabled = local.enabled && var.lambda_at_edge_enabled
89
}
910

1011
data "aws_partition" "current" {
@@ -98,6 +99,9 @@ module "cloudfront_s3_cdn" {
9899
failover_criteria = var.origin_group_failover_criteria_status_codes
99100
}], local.additional_origin_groups)
100101

102+
lambda_function_association = local.lambda_at_edge_enabled ? module.lambda_at_edge.lambda_function_association : []
103+
forward_header_values = local.lambda_at_edge_enabled ? ["useless-header"] : []
104+
101105
context = module.this.context
102106
}
103107

examples/complete/outputs.tf

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,3 +62,8 @@ output "s3_bucket_policy" {
6262
value = module.cloudfront_s3_cdn.s3_bucket_policy
6363
description = "Final computed S3 bucket policy"
6464
}
65+
66+
output "lambda_function_association" {
67+
description = "The Lambda@Edge function association configuration."
68+
value = module.lambda_at_edge.lambda_function_association
69+
}

examples/complete/variables.tf

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,3 +29,9 @@ variable "origin_group_failover_criteria_status_codes" {
2929
502
3030
]
3131
}
32+
33+
variable "lambda_at_edge_enabled" {
34+
type = bool
35+
description = "Whether or not to enable Lambda@Edge functions."
36+
default = false
37+
}

examples/complete/versions.tf

Lines changed: 1 addition & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -4,19 +4,7 @@ terraform {
44
required_providers {
55
aws = {
66
source = "hashicorp/aws"
7-
version = ">= 2.0"
8-
}
9-
null = {
10-
source = "hashicorp/null"
11-
version = ">= 2.0"
12-
}
13-
template = {
14-
source = "hashicorp/template"
15-
version = ">= 2.0"
16-
}
17-
local = {
18-
source = "hashicorp/local"
19-
version = ">= 1.2"
7+
version = ">= 3.0"
208
}
219
}
2210
}

0 commit comments

Comments
 (0)