Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

build lambda package and deploy lambda function with terraform #1

Merged
merged 1 commit into from
Jan 27, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
TF_S3_BUCKET:=
TF_BACKEND_KEY:=event-to-slack-lambda-layer/terraform.tfstate
TF_REGION:=ap-northeast-1

all: apply

.PHONY: init apply destroy clean

init:
terraform init -backend=true \
-backend-config="bucket=$(TF_S3_BUCKET)" \
-backend-config="key=$(TF_BACKEND_KEY)" \
-backend-config="region=$(TF_REGION)"

apply: init
terraform apply

destroy: init
terraform destroy

invoke:
aws lambda invoke --function-name $(terraform output -json | jq .lambda_function_name.value -r) output.json

clean:
rm -rf package
rm -rf build
17 changes: 17 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# Usage

## Preparation
Create a S3 bucket for tfstate.
```
aws s3 mb s3://<Your own s3 bucket name>
```

## Deploy
```shell
make TF_S3_BUCKET=<Your own s3 bucket> apply
```

## Cleanup
```shell
make TF_S3_BUCKET=<Your own s3 bucket> destroy
```
25 changes: 25 additions & 0 deletions deployments/check-source-code-hash.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
#!/bin/bash
# Ref. https://www.terraform.io/docs/providers/external/data_source.html

# Exit if any of the intermediate steps fail
set -e

# Extract "foo" and "baz" arguments from the input into
# FOO and BAZ shell variables.
# jq will ensure that the values are properly quoted
# and escaped for consumption by the shell.
eval "$(jq -r '@sh "PACKAGE_FILE=\(.package_file)"')"

# Placeholder for whatever data-fetching logic your script implements
if [ ! -e $PACKAGE_FILE ]; then
mkdir $(dirname $PACKAGE_FILE)
# Generate empty file if there is no lambda package. Terraform will create a proper package later on.
touch $PACKAGE_FILE
fi
# check lambda package and detect any code changes in 'src' directory
SHA256=$(find $PACKAGE_FILE src -type f -print0 | sort -z | xargs -0 sha1sum | sha1sum | awk '{ print $1 }')

# Safely produce a JSON object containing the result value.
# jq will ensure that the value is properly quoted
# and escaped to produce a valid JSON string.
jq -n --arg sha256 "$SHA256" '{"sha256":$sha256}'
18 changes: 18 additions & 0 deletions deployments/create-lambda-package.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
#!/bin/bash
set -e

FUNCTION_NAME=$1
PYTHON_VERSION=$2 # '3.6', '3.7', etc...
BUILD_DIR='build'
PACKAGE_DIR='package'

mkdir -p build # temporary directory for venv
mkdir -p package # source directory that will be zipped up by terraform

# source files
cp -r ./src/* ./${PACKAGE_DIR}

# pip packages
python${PYTHON_VERSION} -m venv ${BUILD_DIR}/${FUNCTION_NAME}
. ${BUILD_DIR}/${FUNCTION_NAME}/bin/activate
pip3 install -r ./src/requirements.txt -t ./${PACKAGE_DIR}
53 changes: 53 additions & 0 deletions main.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
data "aws_caller_identity" "current" {}
data "aws_region" "current" {}

locals {
function_name = "event-to-slack"
python_version = "3.7"
lambda_package_file = "package/${local.function_name}.zip"
source_dir = "${path.module}/package"
}

# generate source code hash depending on lambda_package_file and source code in 'src' directory
# Note that any code changes in src directory changes the result of source code hash
# Ref. https://github.com/hashicorp/terraform/issues/10878#issuecomment-453241734
data "external" "source_code_hash" {
program = ["bash", "deployments/check-source-code-hash.sh"]

query = {
package_file = "${local.lambda_package_file}"
}
}

# Ref. https://github.com/hashicorp/terraform/issues/8344#issuecomment-345807204
resource "null_resource" "create_lambda_package" {
triggers {
src_hash = "${data.external.source_code_hash.result["sha256"]}"
}

provisioner "local-exec" {
command = "./deployments/create-lambda-package.sh ${local.function_name} ${local.python_version}"
}
}

# NOTE: 'terrraform plan' does not evaluate 'data'.
# As a result, you always see a change of source_code_hash in plan but it won't happen in apply
# if there is no code change in 'src' directory.
# Ref. https://github.com/hashicorp/terraform/issues/17034
data "archive_file" "lambda_zip_package" {
type = "zip"
source_dir = "${local.source_dir}"
output_path = "${path.module}/${local.lambda_package_file}"

depends_on = ["null_resource.create_lambda_package"]
}

resource "aws_lambda_function" "event_to_slack" {
filename = "${local.lambda_package_file}"
source_code_hash = "${data.archive_file.lambda_zip_package.output_base64sha256}"
function_name = "${local.function_name}"
role = "arn:aws:iam::${data.aws_caller_identity.current.account_id}:role/lambda_basic_execution"
handler = "main.handler"
runtime = "python${local.python_version}"
timeout = 300
}
12 changes: 12 additions & 0 deletions src/main.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import boto3
import subprocess


def handler(event, option):
print("hello, world!")
opt = (
subprocess.run(["find", "/opt"], capture_output=True)
.stdout.decode("utf-8")
.split("\n")
)
return {"statusCode": 200, "body": "Hi!", "opt": opt}
1 change: 1 addition & 0 deletions src/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
boto3
10 changes: 10 additions & 0 deletions terraform.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
provider "aws" {
version = "~> 1.57.0"
region = "ap-northeast-1"
}

terraform {
required_version = "= 0.11.11"

backend "s3" {}
}