Deploying AWS Lambda Function with Terraform + Custom Dependencies

AWS Lambda Functions
  • How to deploy Lambda function on Amazon Web Services.
  • How to use Terraform to deploy our project infrastructure.
  • How integrate S3 notifications with our Lambda Function.
  • How to package our Lambda Function with all ours dependencies and deploy it to AWS.
aws-projects/
lambda_function/
__init__.py
lambda.py
requirements.txt

scripts/
create_pkg.sh

.gitignore
buckets.tf
iam.tf
lambdas.tf
variables.tf

Terraform

Starting writing the Lambda Function

import json
import requests

from json_checker import Checker


def lambda_handler(event, context):
current_data = {'first_key': 1, 'second_key': '2'}
expected_schema = {'first_key': int, 'second_key': str}
checker = Checker(expected_schema)

s3_events = []
for record in event['Records']:
s3_events.append({
"bucket": record['s3']['bucket']['name'],
"file": record['s3']['object']['key']
})

data = {
"result": checker.validate(current_data),
"s3_events": s3_events
}

try:
response = requests.post(
'https://python-lambda.free.beeceptor.com/my/api/path',
data=json.dumps(data)
)

except Exception as error:
print('Error in request: ', str(error))

return {
"statusCode": 200,
"body": json.dumps({
"result": checker.validate(current_data),
"s3_events": s3_events
}),
}
json-checker==2.0.0
requests

Defining AWS resources with Terraform

variable "path_source_code" {
default = "lambda_function"
}

variable "function_name" {
default = "aws_lambda_test"
}

variable "runtime" {
default = "python3.7"
}

variable "output_path" {
description = "Path to function's deployment package into local filesystem. eg: /path/lambda_function.zip"
default = "lambda_function.zip"
}

variable "distribution_pkg_folder" {
description = "Folder name to create distribution files..."
default = "lambda_dist_pkg"
}

variable "bucket_for_videos" {
description = "Bucket name for put videos to process..."
default = "aws-lambda-function-read-videos"
}
resource "aws_iam_role" "lambda_exec_role" {
name = "lambda_exec_role"
assume_role_policy = <<EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "sts:AssumeRole",
"Principal": {
"Service": "lambda.amazonaws.com"
}
}
]
}
EOF
}

data "aws_iam_policy_document" "lambda_policy_doc" {
statement {
sid = "AllowInvokingLambdas"
effect = "Allow"

resources = [
"arn:aws:lambda:*:*:function:*"
]

actions = [
"lambda:InvokeFunction"
]
}

statement {
sid = "AllowCreatingLogGroups"
effect = "Allow"

resources = [
"arn:aws:logs:*:*:*"
]

actions = [
"logs:CreateLogGroup"
]
}

statement {
sid = "AllowWritingLogs"
effect = "Allow"

resources = [
"arn:aws:logs:*:*:log-group:/aws/lambda/*:*"
]

actions = [
"logs:CreateLogStream",
"logs:PutLogEvents",
]
}
}

resource "aws_iam_policy" "lambda_iam_policy" {
name = "lambda_iam_policy"
policy = data.aws_iam_policy_document.lambda_policy_doc.json
}

resource "aws_iam_role_policy_attachment" "lambda_policy_attachment" {
policy_arn = aws_iam_policy.lambda_iam_policy.arn
role = aws_iam_role.lambda_exec_role.name
}
resource "aws_s3_bucket" "bucket_read_videos" {
bucket = var.bucket_for_videos
}

resource "aws_s3_bucket_notification" "bucket_notification" {
bucket = aws_s3_bucket.bucket_read_videos.id

lambda_function {
lambda_function_arn = aws_lambda_function.aws_lambda_test.arn
events = ["s3:ObjectCreated:*"]
filter_suffix = ".mp4"
}
}
resource "null_resource" "install_python_dependencies" {
provisioner "local-exec" {
command = "bash ${path.module}/scripts/create_pkg.sh"

environment = {
source_code_path = var.path_source_code
function_name = var.function_name
path_module = path.module
runtime = var.runtime
path_cwd = path.cwd
}
}
}

data "archive_file" "create_dist_pkg" {
depends_on = ["null_resource.install_python_dependencies"]
source_dir = "${path.cwd}/lambda_dist_pkg/"
output_path = var.output_path
type = "zip"
}

resource "aws_lambda_function" "aws_lambda_test" {
function_name = var.function_name
description = "Process video and does face recognition..."
handler = "lambda_function.lambda.lambda_handler"
runtime = var.runtime

role = aws_iam_role.lambda_exec_role.arn
memory_size = 128
timeout = 300

depends_on = [null_resource.install_python_dependencies]
source_code_hash = data.archive_file.create_dist_pkg.output_base64sha256
filename = data.archive_file.create_dist_pkg.output_path
}

resource "aws_lambda_permission" "allow_bucket" {
function_name = aws_lambda_function.aws_lambda_test.arn
source_arn = aws_s3_bucket.bucket_read_videos.arn
statement_id = "AllowExecutionFromS3Bucket"
action = "lambda:InvokeFunction"
principal = "s3.amazonaws.com"
}
#!/bin/bash

echo "Executing create_pkg.sh..."

cd $path_cwd
dir_name=lambda_dist_pkg/
mkdir $dir_name

# Create and activate virtual environment...
virtualenv -p $runtime env_$function_name
source $path_cwd/env_$function_name/bin/activate

# Installing python dependencies...
FILE=$path_cwd/lambda_function/requirements.txt

if [ -f "$FILE" ]; then
echo "Installing dependencies..."
echo "From: requirement.txt file exists..."
pip install -r "$FILE"

else
echo "Error: requirement.txt does not exist!"
fi

# Deactivate virtual environment...
deactivate

# Create deployment package...
echo "Creating deployment package..."
cd env_$function_name/lib/$runtime/site-packages/
cp -r . $path_cwd/$dir_name
cp -r $path_cwd/lambda_function/ $path_cwd/$dir_name

# Removing virtual environment folder...
echo "Removing virtual environment folder..."
rm -rf $path_cwd/env_$function_name

echo "Finished script execution!"
$ terraform init
$ terraform apply
$ terraform destroy

--

--

--

Cloud & Solutions & Data Architect | Python Developer | Serverless Advocate

Love podcasts or audiobooks? Learn on the go with our new app.

Recommended from Medium

The value of a software engineer

remote-trait-object: One of CodeChain Foundry’s core inter-module communication systems

Terraform ‘for_each’ meta-argument

Connecting SQL Server, Oracle, MySQL and PostgreSQL from Azure Services using Python

My attempt to run VS Code on iPad

What is a Webhook?

Agile Software Development — Overcoming Communication and Cultural Challenges

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
Alejandro Cora González

Alejandro Cora González

Cloud & Solutions & Data Architect | Python Developer | Serverless Advocate

More from Medium

The Fastest way to load Files into AWS S3 Buckets: A Guide to Using Amazon Command Line Interface

Docker — How To Run A Container

Docker: Containers For absolute Beginners

AWS Lambda Function using Python packaging with serverless framework