Skip to content

Commit 14f2f4f

Browse files
Refactor lambda source code (#7)
* (remove): upload code from s3 function * (fix): ref to non-declare resource * (add): new idea to manage terraform * (update): source code to new algo * (fix): lambda edge variable overflow * (update): document * (update): README.md * (remove): comment path * (update): change additional policies to map instead of list deu to un-predicted variable * (update): s3 version to v1.0.4 * (add): local tag to ssm parameter store * (add): tracing mode enable * (add): change log * (fix): set default tracing mode if received from upstream * (update): order of variables * (update): CHANGELOG * (update): README * chore: add community friendly templates * (update): CHANGELOG * (update): .gitignore * (update): variable name and README * (update): variable name and README * (update): README * (update): variables naming * (add): complete example * (update): repo name * (update): example for simple usage * (update): example simple * (update): example simple * (update): lambda complete usage * (update): lambda complete usage * (update): README and CHANGELOG * (update): README and CHANGELOG Co-authored-by: Pongsak Sanguanwong <pongsak.sanguanwong@gmail.com>
1 parent 5eedc87 commit 14f2f4f

File tree

14 files changed

+302
-165
lines changed

14 files changed

+302
-165
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ crash.*.log
2424
# to change depending on the environment.
2525
#
2626
*.tfvars
27+
!terraform.*example*.tfvars
2728

2829
# Ignore override files as they are usually used to override resources locally and so
2930
# are not checked in

.pre-commit-config.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ repos:
1111
args:
1212
- "--args=--only=terraform_deprecated_interpolation"
1313
- "--args=--only=terraform_deprecated_index"
14-
# - "--args=--only=terraform_unused_declarations"
14+
- "--args=--only=terraform_unused_declarations"
1515
- "--args=--only=terraform_comment_syntax"
1616
- "--args=--only=terraform_documented_outputs"
1717
- "--args=--only=terraform_documented_variables"

CHANGELOG.md

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
# Change Log
2+
3+
All notable changes to this module will be documented in this file.
4+
5+
## [1.1.0] - 2022-07-22
6+
7+
### Changed
8+
9+
- Remove upload code from s3
10+
- S3 source code is used for versioning
11+
- Change `additional_lambda_role_policy_arn` to map from list
12+
13+
### Added
14+
15+
- Enable Tracing
16+
17+
## [v1.0.2] - 2022-07-01
18+
19+
### Added
20+
21+
- Add default log retention 90 days, KMS encryption support
22+
23+
### Fixed
24+
25+
- Fix kms security issue by @xshot9011 in #9
26+
27+
## [v1.0.1] - 2022-06-08
28+
29+
### Added
30+
31+
- Add resource base policy for lambda
32+
33+
## [v1.0.0] - 2022-05-17
34+
35+
### Added
36+
37+
- Since Lambdas are uploaded via zip files, we generate a zip file from the path specified.
38+
- Upload the zip file containing the build artifacts to S3.
39+
- Allow access to this lambda function from AWS.
40+
- Allow lambda to generate logs.
41+
- Construct a role that AWS services can adopt in order to invoke our function.
42+
- This policy also has the capability to write logs to CloudWatch.
43+
- Create the secret SSM parameters that can be retrieved and decoded by the lambda function.
44+
- Create an IAM policy document granting the ability to read and retrieve SSM parameter values.
45+
- Develop a policy based on the SSM policy paper
46+
- Custom policies to attach to this role

README.md

Lines changed: 39 additions & 101 deletions
Large diffs are not rendered by default.

examples/complete/main.tf

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
module "lambda" {
2+
source = "../../"
3+
4+
prefix = "oozou"
5+
environment = "devops"
6+
name = "demo"
7+
8+
is_edge = false # Defautl is `false`, If you want to publish to the edge don't forget to override aws's provider to virgina
9+
10+
# If is_edge is `false`, ignore this config
11+
is_create_lambda_bucket = true # Default is `false`; plz use false, if not 1 lambda: 1 bucket
12+
bucket_name = "" # If `is_create_lambda_bucket` is `false`; specified this, default is `""`
13+
14+
# Source code
15+
source_code_dir = "./src"
16+
file_globs = ["index.js"]
17+
compressed_local_file_dir = "./outputs"
18+
19+
# Lambda Env
20+
runtime = "nodejs12.x"
21+
handler = "index.handler"
22+
23+
# Lambda Specification
24+
timeout = 3
25+
memory_size = 128
26+
reserved_concurrent_executions = -1
27+
28+
# Optional to connect Lambda to VPC
29+
vpc_config = {
30+
security_group_ids = ["sg-028f637312eea735e"]
31+
subnet_ids_to_associate = ["subnet-0b853f8c85796d72d", "subnet-07c068b4b51262793", "subnet-0362f68c559ef7716"]
32+
}
33+
dead_letter_target_arn = "arn:aws:sns:ap-southeast-1:557291035693:demo" # To send failed processing to target, Default is `""`
34+
35+
# IAM
36+
is_create_lambda_role = true # Default is `true`
37+
lambda_role_arn = "" # If `is_create_lambda_role` is `false`
38+
# The policies that you want to attach to IAM Role created by only this module # If `is_create_lambda_role` is `false`
39+
additional_lambda_role_policy_arns = {
40+
allow_lambda_to_read_s3 = "arn:aws:iam::aws:policy/AmazonS3ReadOnlyAccess"
41+
}
42+
43+
# Resource policy
44+
lambda_permission_configurations = {
45+
lambda_on_my_account = {
46+
pricipal = "apigateway.amazonaws.com"
47+
source_arn = "arn:aws:execute-api:ap-southeast-1:557291035112:lk36vflbha/*/*/"
48+
}
49+
lambda_on_my_another_account_wrong = {
50+
pricipal = "apigateway.amazonaws.com"
51+
source_arn = "arn:aws:execute-api:ap-southeast-1:224563527112:q6pwa6wgr6/*/*/"
52+
source_account = "557291035112"
53+
}
54+
lambda_on_my_another_account_correct = {
55+
pricipal = "apigateway.amazonaws.com"
56+
source_arn = "arn:aws:execute-api:ap-southeast-1:557291035112:wpj4t3scmb/*/*/"
57+
}
58+
}
59+
60+
# Logging
61+
is_create_cloudwatch_log_group = true # Default is `true`
62+
cloudwatch_log_retention_in_days = 90 # Default is `90`
63+
64+
# Env
65+
ssm_params = {}
66+
plaintext_params = {
67+
region = "ap-southeast-1"
68+
cluster_name = "oozou-dev-test-schedule-cluster"
69+
nodegroup_name = "oozou-dev-test-schedule-custom-nodegroup"
70+
min = 1,
71+
max = 1,
72+
desired = 1
73+
}
74+
75+
tags = { "Workspace" = "900-oozou-sandbox-terraform" }
76+
}

examples/complete/outputs.tf

Whitespace-only changes.

examples/complete/src/index.js

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
var http = require('http')
2+
3+
exports.handler = (event, context, callback) => {
4+
const options = {
5+
hostname: event.Host,
6+
port: event.Port
7+
}
8+
9+
const response = {};
10+
11+
http.get(options, (res) => {
12+
response.httpStatus = res.statusCode
13+
callback(null, response)
14+
}).on('error', (err) => {
15+
callback(null, err.message);
16+
})
17+
18+
};

examples/complete/variables.tf

Whitespace-only changes.

examples/simple/main.tf

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
module "lambda" {
2+
source = "../../"
3+
4+
prefix = "oozou"
5+
environment = "devops"
6+
name = "demo"
7+
8+
source_code_dir = "./src"
9+
file_globs = ["index.js"]
10+
compressed_local_file_dir = "./outputs"
11+
12+
runtime = "nodejs12.x"
13+
handler = "index.handler"
14+
15+
additional_lambda_role_policy_arns = {
16+
allow_lambda_to_read_s3 = "arn:aws:iam::aws:policy/AmazonS3ReadOnlyAccess"
17+
}
18+
lambda_permission_configurations = {
19+
lambda_on_my_account = {
20+
pricipal = "apigateway.amazonaws.com"
21+
source_arn = "arn:aws:execute-api:ap-southeast-1:557291035112:lk36vflbha/*/*/"
22+
}
23+
lambda_on_my_another_account_wrong = {
24+
pricipal = "apigateway.amazonaws.com"
25+
source_arn = "arn:aws:execute-api:ap-southeast-1:224563527112:q6pwa6wgr6/*/*/"
26+
source_account = "557291035112"
27+
}
28+
lambda_on_my_another_account_correct = {
29+
pricipal = "apigateway.amazonaws.com"
30+
source_arn = "arn:aws:execute-api:ap-southeast-1:557291035112:wpj4t3scmb/*/*/"
31+
}
32+
}
33+
34+
ssm_params = {}
35+
plaintext_params = {
36+
region = "ap-southeast-1"
37+
cluster_name = "oozou-dev-test-schedule-cluster"
38+
nodegroup_name = "oozou-dev-test-schedule-custom-nodegroup"
39+
min = 1,
40+
max = 1,
41+
desired = 1
42+
}
43+
44+
tags = { "Workspace" = "900-oozou-sandbox-terraform" }
45+
}

examples/simple/outputs.tf

Whitespace-only changes.

examples/simple/src/index.js

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
var http = require('http')
2+
3+
exports.handler = (event, context, callback) => {
4+
const options = {
5+
hostname: event.Host,
6+
port: event.Port
7+
}
8+
9+
const response = {};
10+
11+
http.get(options, (res) => {
12+
response.httpStatus = res.statusCode
13+
callback(null, response)
14+
}).on('error', (err) => {
15+
callback(null, err.message);
16+
})
17+
18+
};

examples/simple/variables.tf

Whitespace-only changes.

main.tf

Lines changed: 40 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,12 @@
44
locals {
55
name = format("%s-%s-%s", var.prefix, var.environment, var.name)
66

7-
lambda_role_arn = var.is_create_lambda_role ? aws_iam_role.this[0].arn : var.lambda_role_arn
8-
bucket_name = var.is_upload_form_s3 ? var.bucket_name : var.is_create_lambda_bucket ? element(module.s3[*].bucket_name, 0) : var.bucket_name
9-
object_key = var.is_upload_form_s3 ? data.aws_s3_object.this[0].key : aws_s3_object.this[0].id
10-
object_version_id = var.is_upload_form_s3 ? data.aws_s3_object.this[0].version_id : aws_s3_object.this[0].version_id
7+
lambda_role_arn = var.is_create_lambda_role ? aws_iam_role.this[0].arn : var.lambda_role_arn
8+
9+
file_name = var.is_edge ? null : data.archive_file.this.output_path
10+
bucket_name = var.is_edge ? var.is_create_lambda_bucket ? module.s3[0].bucket_name : var.bucket_name : null
11+
object_key = var.is_edge ? aws_s3_object.this[0].id : null
12+
object_version_id = var.is_edge ? aws_s3_object.this[0].version_id : null
1113

1214
tags = merge(
1315
{
@@ -21,20 +23,15 @@ locals {
2123
locals {
2224
raise_is_lambda_role_arn_empty = var.is_create_lambda_role == false && var.lambda_role_arn == "" ? file("Variable `lambda_role_arn` is required when `is_create_lambda_role` is false") : "pass"
2325

24-
raise_bucket_name_empty = var.is_upload_form_s3 && length(var.bucket_name) == 0 ? file("Variable `bucket_name` is required when `is_upload_form_s3` is true") : "pass"
25-
raise_file_name_empty = var.is_upload_form_s3 && length(var.file_name) == 0 ? file("Variable `file_name` is required when `is_upload_form_s3` is true") : "pass"
26-
27-
raise_compressed_local_file_dir_empty = var.is_upload_form_s3 == false && length(var.compressed_local_file_dir) == 0 ? file("Variable `compressed_local_file_dir` is required when `is_upload_form_s3` is false") : "pass"
28-
raise_file_globs_empty = var.is_upload_form_s3 == false && length(var.file_globs) == 0 ? file("Variable `file_globs` is required when `is_upload_form_s3` is false") : "pass"
26+
raise_bucket_name_empty = var.is_edge && var.is_create_lambda_bucket == false && length(var.bucket_name) == 0 ? file("Variable `bucket_name` is required when `is_create_lambda_bucket` is false") : "pass"
27+
raise_local_file_dir_empty = length(var.compressed_local_file_dir) == 0 ? file("Variable `compressed_local_file_dir` is required") : "pass"
28+
raise_file_globs_empty = length(var.file_globs) == 0 ? file("Variable `file_globs` is required") : "pass"
2929
}
3030

3131
/* -------------------------------------------------------------------------- */
32-
/* S3 */
32+
/* Zip File */
3333
/* -------------------------------------------------------------------------- */
34-
/* -------------------------------- ZIP File -------------------------------- */
35-
data "archive_file" "zip_file" {
36-
count = var.is_upload_form_s3 == false ? 1 : 0
37-
34+
data "archive_file" "this" {
3835
type = "zip"
3936
output_path = format("%s/%s.zip", var.compressed_local_file_dir, local.name)
4037

@@ -63,10 +60,13 @@ data "archive_file" "zip_file" {
6360
}
6461
}
6562

63+
/* -------------------------------------------------------------------------- */
64+
/* S3 */
65+
/* -------------------------------------------------------------------------- */
6666
module "s3" {
67-
count = var.is_create_lambda_bucket && var.is_upload_form_s3 == false ? 1 : 0
67+
count = var.is_edge && var.is_create_lambda_bucket ? 1 : 0
6868

69-
source = "git@github.com:oozou/terraform-aws-s3.git?ref=v1.0.2"
69+
source = "git@github.com:oozou/terraform-aws-s3.git?ref=v1.0.4"
7070

7171
prefix = var.prefix
7272
environment = var.environment
@@ -80,20 +80,13 @@ module "s3" {
8080
tags = var.tags
8181
}
8282

83-
data "aws_s3_object" "this" {
84-
count = var.is_upload_form_s3 ? 1 : 0
85-
86-
bucket = local.bucket_name
87-
key = var.file_name
88-
}
89-
9083
resource "aws_s3_object" "this" {
91-
count = var.is_upload_form_s3 == false ? 1 : 0
84+
count = var.is_edge && var.is_create_lambda_bucket ? 1 : 0
9285

93-
bucket = var.is_create_lambda_bucket ? element(module.s3[*].bucket_name, 0) : var.bucket_name
86+
bucket = element(module.s3[*].bucket_name, 0)
9487
key = format("%s.zip", local.name)
95-
source = data.archive_file.zip_file[0].output_path
96-
etag = data.archive_file.zip_file[0].output_md5
88+
source = data.archive_file.this.output_path
89+
etag = data.archive_file.this.output_md5
9790

9891
tags = merge(local.tags, { "Name" = format("%s.zip", local.name) })
9992
}
@@ -102,7 +95,7 @@ resource "aws_s3_object" "this" {
10295
/* Resource Based Policy */
10396
/* -------------------------------------------------------------------------- */
10497
resource "aws_lambda_permission" "allow_serivce" {
105-
for_each = var.lambda_permission_configuration
98+
for_each = var.lambda_permission_configurations
10699

107100
statement_id = format("AllowExecutionFrom-%s", each.key)
108101
action = "lambda:InvokeFunction"
@@ -199,7 +192,7 @@ resource "aws_iam_role_policy" "logs_role_policy" {
199192
}
200193

201194
resource "aws_iam_role_policy_attachment" "this" {
202-
for_each = var.is_create_lambda_role ? toset(var.additional_lambda_role_policy_arns) : toset([])
195+
for_each = var.is_create_lambda_role ? var.additional_lambda_role_policy_arns : {}
203196

204197
role = aws_iam_role.this[0].name
205198
policy_arn = each.value
@@ -219,7 +212,7 @@ resource "aws_ssm_parameter" "params" {
219212
type = "SecureString"
220213
tier = length(each.value) > 4096 ? "Advanced" : "Standard"
221214

222-
tags = var.tags
215+
tags = local.tags
223216
}
224217

225218
data "aws_iam_policy_document" "secret_access_policy_doc" {
@@ -261,16 +254,27 @@ resource "aws_lambda_function" "this" {
261254
function_name = format("%s-function", local.name)
262255
description = format("Lambda function: %s", local.name)
263256

264-
# Read the file from s3
257+
# Read source code from s3
265258
s3_bucket = local.bucket_name
266259
s3_key = local.object_key
267260
s3_object_version = local.object_version_id
268261

262+
# Read source code from local
263+
filename = local.file_name
264+
source_code_hash = filebase64sha256(data.archive_file.this.output_path)
265+
269266
# Specification
270267
timeout = var.timeout
271268
memory_size = var.memory_size
272269
reserved_concurrent_executions = var.reserved_concurrent_executions
273270

271+
# Code Env
272+
publish = true # Force public new version
273+
runtime = var.runtime
274+
handler = var.handler
275+
276+
role = local.lambda_role_arn
277+
274278
vpc_config {
275279
security_group_ids = var.vpc_config.security_group_ids
276280
subnet_ids = var.vpc_config.subnet_ids_to_associate
@@ -284,17 +288,11 @@ resource "aws_lambda_function" "this" {
284288
}
285289
}
286290

287-
# Code Env
288-
publish = true # Force public new version
289-
runtime = var.runtime
290-
handler = var.handler
291-
292-
role = local.lambda_role_arn
293-
294-
lifecycle {
295-
ignore_changes = [
296-
last_modified,
297-
]
291+
dynamic "tracing_config" {
292+
for_each = var.tracing_mode == null ? [] : [true]
293+
content {
294+
mode = var.tracing_mode
295+
}
298296
}
299297

300298
tags = merge(local.tags, { "Name" = format("%s-function", local.name) })

0 commit comments

Comments
 (0)