Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save Paulo-Lopes-Estevao/3ac5fe6420f1d5bd9cdd91266b52d440 to your computer and use it in GitHub Desktop.
Save Paulo-Lopes-Estevao/3ac5fe6420f1d5bd9cdd91266b52d440 to your computer and use it in GitHub Desktop.
AWS Lambda and API Gateway using Terraform.
AWS Lambda functions and API gateway are often used to create serverless applications.
Function code is written in Go and deployed using Terraform.
resource "aws_apigatewayv2_api" "lambda_api_gateway" {
name = var.apigatewayv2_api_main.name
protocol_type = var.apigatewayv2_api_main.protocol_type
}
resource "aws_apigatewayv2_stage" "lambda_api_gateway_stage" {
api_id = aws_apigatewayv2_api.lambda_api_gateway.id
name = "dev"
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"
}
)
}
}
resource "aws_apigatewayv2_integration" "lambda_api_gateway_integration" {
api_id = aws_apigatewayv2_api.lambda_api_gateway.id
integration_type = "AWS_PROXY"
integration_method = "POST"
integration_uri = aws_lambda_function.lambda_handler.invoke_arn
}
resource "aws_apigatewayv2_route" "get_lambda_api_gateway_route" {
api_id = aws_apigatewayv2_api.lambda_api_gateway.id
route_key = "GET /hello"
target = "integrations/${aws_apigatewayv2_integration.lambda_api_gateway_integration.id}"
}
resource "aws_cloudwatch_log_group" "api_gw" {
name = "/aws/apigateway/${aws_apigatewayv2_api.lambda_api_gateway.name}"
retention_in_days = 7
}
resource "aws_lambda_permission" "lambda_api_gateway_permission" {
statement_id = "AllowExecutionFromAPIGateway"
action = "lambda:InvokeFunction"
function_name = aws_lambda_function.lambda_handler.function_name
principal = "apigateway.amazonaws.com"
source_arn = "${aws_apigatewayv2_api.lambda_api_gateway.execution_arn}/*/*"
}
resource "aws_s3_bucket" "lambda_bucket" {
bucket = random_pet.lambda_bucket_name.id
}
resource "aws_s3_bucket_ownership_controls" "lambda_bucket_ownership_controls" {
bucket = aws_s3_bucket.lambda_bucket.id
rule {
object_ownership = "BucketOwnerPreferred"
}
}
resource "aws_s3_bucket_acl" "lambda_bucket_acl" {
depends_on = [aws_s3_bucket_ownership_controls.lambda_bucket_ownership_controls]
bucket = aws_s3_bucket.lambda_bucket.id
acl = "private"
}
resource "aws_s3_object" "lambda_handler_bucket_object" {
bucket = aws_s3_bucket.lambda_bucket.id
key = "handler.zip"
source = data.archive_file.lambda_handler_archive_file.output_path
etag = filemd5(data.archive_file.lambda_handler_archive_file.output_path)
depends_on = [
data.archive_file.lambda_handler_archive_file,
]
}
resource "aws_lambda_function" "lambda_handler" {
function_name = "LambdaHandler"
role = aws_iam_role.lambda_role.arn
handler = "app"
source_code_hash = data.archive_file.lambda_handler_archive_file.output_base64sha256 // or filebase64sha256(handler.zip)
runtime = "go1.x"
s3_bucket = aws_s3_bucket.lambda_bucket.id
s3_key = aws_s3_object.lambda_handler_bucket_object.key
depends_on = [
aws_s3_object.lambda_handler_bucket_object,
]
}
resource "aws_cloudwatch_log_group" "lambda_cloudwatch_log" {
name = "/aws/lambda/${aws_lambda_function.lambda_handler.function_name}"
retention_in_days = 14
}
package main
import (
"context"
"encoding/json"
"github.com/aws/aws-lambda-go/events"
"github.com/aws/aws-lambda-go/lambda"
"log"
"net/http"
)
type Africa struct {
Flag string `json:"flag"`
Location string `json:"location"`
Keywords []string `json:"keywords"`
Name string `json:"name"`
Capital string `json:"capital"`
Currency string `json:"currency"`
Language string `json:"language"`
Population float64 `json:"population"`
Area float64 `json:"area"`
Callingcode string `json:"callingcode"`
}
func HandleRequest(ctx context.Context, request events.APIGatewayProxyRequest) (*events.APIGatewayV2HTTPResponse, error) {
log.Println("Start handler")
var africa []Africa
err := json.Unmarshal(data, &africa)
if err != nil {
return errResponse(http.StatusInternalServerError, err.Error()), nil
}
return response(http.StatusOK, africa), nil
}
func response(code int, data []Africa) *events.APIGatewayV2HTTPResponse {
marshalled, err := json.Marshal(data)
if err != nil {
return errResponse(http.StatusInternalServerError, err.Error())
}
return &events.APIGatewayV2HTTPResponse{
StatusCode: code,
Headers: map[string]string{
"Content-Type": "application/json",
},
Body: string(marshalled),
IsBase64Encoded: false,
}
}
func errResponse(status int, body string) *events.APIGatewayV2HTTPResponse {
message := map[string]string{
"message": body,
}
messageBytes, _ := json.Marshal(&message)
return &events.APIGatewayV2HTTPResponse{
StatusCode: status,
Headers: map[string]string{
"Content-Type": "application/json",
},
Body: string(messageBytes),
}
}
func main() {
log.Println("Start lambda")
lambda.Start(HandleRequest)
}
var data = []byte(`
[
{
"flag": "https://upload.wikimedia.org/wikipedia/commons/thumb/a/af/Flag_of_South_Africa.svg/200px-Flag_of_South_Africa.svg.png",
"location": "https://upload.wikimedia.org/wikipedia/commons/thumb/a/a1/LocationSouthAfrica.svg/300px-LocationSouthAfrica.svg.png",
"keywords": ["ZA", "South Africa"],
"name": "África do Sul",
"capital": "Cape Town",
"currency": "Rand",
"language": "Inglês",
"population": 0,
"area": 0,
"callingcode": "+27"
},
{
"flag": "https://upload.wikimedia.org/wikipedia/commons/thumb/9/9d/Flag_of_Angola.svg/200px-Flag_of_Angola.svg.png",
"location": "https://upload.wikimedia.org/wikipedia/commons/thumb/c/c5/LocationAngola.svg/300px-LocationAngola.svg.png",
"keywords": ["AO"],
"name": "Angola",
"capital": "Luanda",
"currency": "Kwanza",
"language": "Português",
"population": 0,
"area": 0,
"callingcode": "+244"
},
{
"flag": "https://upload.wikimedia.org/wikipedia/commons/thumb/7/77/Flag_of_Algeria.svg/200px-Flag_of_Algeria.svg.png",
"location": "https://upload.wikimedia.org/wikipedia/commons/thumb/b/b8/LocationAlgeria.svg/300px-LocationAlgeria.svg.png",
"keywords": ["DZ"],
"name": "Argélia",
"capital": "Algiers",
"currency": "Dinar argelino",
"language": "Árabe",
"population": 0,
"area": 0,
"callingcode": "+213"
}
]`)
provider "aws" {
region = var.aws_region
default_tags {
tags = {
tag_name = "lambda-api-gateway"
}
}
}
data "archive_file" "lambda_handler_archive_file" {
type = "zip"
source_dir = "${path.module}/handler"
output_path = "${path.module}/handler.zip"
}
resource "random_pet" "lambda_bucket_name" {
prefix = "lambda-bucket"
length = 4
}
resource "aws_iam_role" "lambda_role" {
name = "handler-lambda-role"
assume_role_policy = <<EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Action": "sts:AssumeRole",
"Principal": {
"Service": "lambda.amazonaws.com"
},
"Effect": "Allow",
"Sid": ""
}
]
}
EOF
}
resource "aws_iam_role_policy_attachment" "lambda_role_policy" {
role = aws_iam_role.lambda_role.name
policy_arn = var.iam_role_policy_arn
}
build:
GOOS=linux GOARCH=amd64 CGO_ENABLED=0 go build -v -ldflags='-d -s -w' -a -tags netgo -installsuffix netgo -o handler/app handler/main.go && \
chmod +x handler/app
init:
terraform init
validate:
terraform validate
plan:
terraform plan
apply:
terraform apply --auto-approve
destroy:
terraform destroy --auto-approve
.PHONY: build init validate plan apply
LAMBDA_NAME=LambdaHandler
invoke-lambda:
aws lambda invoke --region=us-east-1 --function-name $(LAMBDA_NAME) response.json
output "lambda_role" {
description = "IAM role for the lambda function."
value = aws_iam_role.lambda_role
}
output "lambda_bucket_object" {
description = "S3 bucket object for the lambda handler."
value = aws_s3_object.lambda_handler_bucket_object
}
output "function_name" {
description = "Name of the lambda function."
value = aws_lambda_function.lambda_handler.function_name
}
output "base_url" {
description = "Base URL for the API Gateway v2 API."
value = aws_apigatewayv2_api.lambda_api_gateway.api_endpoint
}
output "get_base_url" {
value = aws_apigatewayv2_stage.lambda_api_gateway_stage.invoke_url
}
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 4.15.0"
}
random = {
source = "hashicorp/random"
version = "~> 3.1.0"
}
archive = {
source = "hashicorp/archive"
version = " 2.4.0"
}
}
required_version = "~> 1.2"
}
variable "aws_region" {
description = "AWS region for all resources."
type = string
default = "us-east-1"
}
variable "iam_role_policy_arn" {
description = "ARN of the IAM role policy to attach to the lambda role."
type = string
default = "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
}
variable "apigatewayv2_api_main" {
description = "API Gateway v2 API for the main API."
type = object({
name = string
protocol_type = string
})
default = {
name = "main"
protocol_type = "HTTP"
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment