Skip to content

Instantly share code, notes, and snippets.

@elvisciotti
Last active November 27, 2022 23:14
Show Gist options
  • Save elvisciotti/a30ab0cd46e2eb16546e74c2199d966b to your computer and use it in GitHub Desktop.
Save elvisciotti/a30ab0cd46e2eb16546e74c2199d966b to your computer and use it in GitHub Desktop.
AWS node function deployed with Terraform - complete example
'use strict';
function shuffle(array) {
let currentIndex = array.length, temporaryValue, randomIndex;
// While there remain elements to shuffle...
while (0 !== currentIndex) {
// Pick a remaining element...
randomIndex = Math.floor(Math.random() * currentIndex);
currentIndex -= 1;
// And swap it with the current element.
temporaryValue = array[currentIndex];
array[currentIndex] = array[randomIndex];
array[randomIndex] = temporaryValue;
}
return array;
}
const QUOTE_ENTRIES = [
'If you are not ashamed of what you were 1 year ago, then you have not improved much',
'Success is determined by how well you manage failures',
'The egg has a perfect shape, even if it\'s laid from the ass',
'Those who know everything learn nothing'
];
function http200Response(event, data) {
return {
statusCode: 200,
body: JSON.stringify({
data: data,
event: event.queryStringParameters
},
null,
2
)
};
}
module.exports.handler = async (event) => {
const action = event.queryStringParameters.action;
const limit = event.queryStringParameters.limit ? parseInt(event.queryStringParameters.limit) : 10;
switch (action) {
case 'random-quote':
const gratitudeItems = shuffle(QUOTE_ENTRIES);
return http200Response(
event,
gratitudeItems.slice(0, limit)
);
case 'today-date':
return http200Response(
event,
new Date()
);
}
return {
statusCode: 404,
body: "action not implemented"
};
};
# https://learn.hashicorp.com/tutorials/terraform/lambda-api-gateway
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 4.0.0"
}
random = {
source = "hashicorp/random"
version = "~> 3.1.0"
}
archive = {
source = "hashicorp/archive"
version = "~> 2.2.0"
}
}
required_version = "~> 1.0"
}
provider "aws" {
region = "us-east-1"
shared_credentials_files = [
"/Users/elvisciotti/.aws/credentials"
]
profile = "elvis"
}
resource "random_pet" "lambda_bucket_name" {
prefix = "ec-notes"
length = 4
}
resource "aws_s3_bucket" "lambda_bucket" {
bucket = random_pet.lambda_bucket_name.id
# bucket = "EcNotesLambdaProd"
# acl = "private"
force_destroy = true
}
#########################################
# zip upload into bucket
#########################################
data "archive_file" "lambda_notes" {
type = "zip"
source_dir = "${path.module}"
output_path = "/tmp/ec-notes.zip"
excludes = [
".git",
".gitattributes",
".gitignore",
".idea",
".jshintrc",
".terraform",
".terraform.lock.hcl"
]
}
resource "aws_s3_object" "lambda_ec_notes" {
bucket = aws_s3_bucket.lambda_bucket.id
key = "ec-notes.zip"
source = data.archive_file.lambda_notes.output_path
etag = filemd5(data.archive_file.lambda_notes.output_path)
}
#########################################
# define your Lambda function and related resources.
#########################################
resource "aws_lambda_function" "ec_notes_random_notes" {
function_name = "EcNotesRandomNotes"
s3_bucket = aws_s3_bucket.lambda_bucket.id
s3_key = aws_s3_object.lambda_ec_notes.key
runtime = "nodejs12.x"
handler = "handler.handler"
source_code_hash = data.archive_file.lambda_notes.output_base64sha256
role = aws_iam_role.lambda_exec.arn
}
# log group to store log messages from your Lambda function for 2 days
resource "aws_cloudwatch_log_group" "ec_random_notes" {
name = "/aws/lambda/${aws_lambda_function.ec_notes_random_notes.function_name}"
retention_in_days = 1
}
# IAM role that allows Lambda to access resources in your AWS account.
resource "aws_iam_role" "lambda_exec" {
name = "serverless_lambda"
assume_role_policy = jsonencode({
Version = "2012-10-17"
Statement = [{
Action = "sts:AssumeRole"
Effect = "Allow"
Sid = ""
Principal = {
Service = "lambda.amazonaws.com"
}
}
]
})
}
# attaches a policy the IAM role
resource "aws_iam_role_policy_attachment" "lambda_policy" {
role = aws_iam_role.lambda_exec.name
policy_arn = "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
}
#########################################
# gateway
#########################################
# defines a name for the API Gateway and sets its protocol to HTTP.
resource "aws_apigatewayv2_api" "lambda" {
name = "serverless_lambda_gw"
protocol_type = "HTTP"
}
# application single stage (test/prod...) for the API Gateway
resource "aws_apigatewayv2_stage" "lambda" {
api_id = aws_apigatewayv2_api.lambda.id
name = "prod"
auto_deploy = true
access_log_settings {
destination_arn = aws_cloudwatch_log_group.api_gw.arn
format = jsonencode({
requestId = "$context.requestId"
sourceIp = "$context.identity.sourceIp"
requestTime = "$context.requestTime"
protocol = "$context.protocol"
httpMethod = "$context.httpMethod"
resourcePath = "$context.resourcePath"
routeKey = "$context.routeKey"
status = "$context.status"
responseLength = "$context.responseLength"
integrationErrorMessage = "$context.integrationErrorMessage"
}
)
}
}
# configures the API Gateway to use your Lambda function.
resource "aws_apigatewayv2_integration" "ec_random_notes" {
api_id = aws_apigatewayv2_api.lambda.id
integration_uri = aws_lambda_function.ec_notes_random_notes.invoke_arn
integration_type = "AWS_PROXY"
integration_method = "POST"
}
# maps an HTTP request to a target, in
resource "aws_apigatewayv2_route" "ec_random_notes" {
api_id = aws_apigatewayv2_api.lambda.id
route_key = "GET /notes"
target = "integrations/${aws_apigatewayv2_integration.ec_random_notes.id}"
}
# log group to store access logs f
resource "aws_cloudwatch_log_group" "api_gw" {
name = "/aws/api_gw/${aws_apigatewayv2_api.lambda.name}"
retention_in_days = 1
}
# API Gateway permission to invoke your Lambda function.
resource "aws_lambda_permission" "api_gw_random_notes" {
statement_id = "AllowExecutionFromAPIGateway"
action = "lambda:InvokeFunction"
function_name = aws_lambda_function.ec_notes_random_notes.function_name
principal = "apigateway.amazonaws.com"
source_arn = "${aws_apigatewayv2_api.lambda.execution_arn}/*/*"
}
# Output value definitions
output "lambda_bucket_name" {
description = "Name of the S3 bucket used to store function code."
value = aws_s3_bucket.lambda_bucket.id
}
output "function_name" {
description = "Name of the EC notes random Lambda function."
value = aws_lambda_function.ec_notes_random_notes.function_name
}
output "base_url_random_quote" {
description = "random quote"
value = "curl ${aws_apigatewayv2_stage.lambda.invoke_url}/notes?action=random-quote"
}
output "base_url_today_date" {
description = "today-date"
value = "curl ${aws_apigatewayv2_stage.lambda.invoke_url}/notes?action=today-date"
}
# init (just once)
terraform init
# preview
terraform plan
# deploy with confirmation
terraform apply
# deploy without confirmation
# terraform apply -auto-approve
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment