Skip to content
Open
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,4 @@
.DS_Store
terraform.tfstate
terraform.tfstate.backup
.terraform
File renamed without changes.
50 changes: 50 additions & 0 deletions terraform/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# howto
Deploy application with terraform

### Table of Contents
**[Pre-deployment](#pre-deployment)**<br>
**[Deployment](#deployment)**<br>
**[Tests](#tests)**<br>


Pre-deployment
---

Zip your python code:

`zip python.zip lambda.py`

Create a aws s3 bucket with location constraint:

`aws s3api create-bucket --bucket=terraform-serverless-python --create-bucket-configuration LocationConstraint=eu-central-1`

Upload your build artifact into newly created bucket:

`aws s3 cp python.zip s3://terraform-serverless-python/v1.0.0/python.zip`


Deployment
---

Deploy your application with your artifact already available in the s3 bucket

`terraform plan -var="app_version=1.0.0"`

`terraform apply -var="app_version=1.0.0"`

Use `terraform output` to get an output of your API Gateway **base_url**:

```python
terraform output
base_url = https://95q1xx0fol.execute-api.eu-central-1.amazonaws.com/v1
```

Tests
---
Test your lambda function via API Gateway with **curl**:

```python
curl -H "Content-Type: application/json" -X POST -d '{"answer":"def sum(x,y):\n return x-y"}' $(terraform output base_url)/pyexecute
```

Update your `assets/main.js` file with new **base_url**.
83 changes: 83 additions & 0 deletions terraform/api_gateway.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
## API Gateway config
resource "aws_api_gateway_rest_api" "PyAPI" {
name = "TfServerlessPythonAPI"
description = "API for Serverless Python Application Example"
}

resource "aws_api_gateway_resource" "ExecutePyResource" {
rest_api_id = "${aws_api_gateway_rest_api.PyAPI.id}"
parent_id = "${aws_api_gateway_rest_api.PyAPI.root_resource_id}"
path_part = "pyexecute"
}

resource "aws_api_gateway_method" "ExecutePyMethod" {
rest_api_id = "${aws_api_gateway_rest_api.PyAPI.id}"
resource_id = "${aws_api_gateway_resource.ExecutePyResource.id}"
http_method = "POST"
authorization = "NONE"
}

## Lambda Integration
resource "aws_api_gateway_integration" "LambdaIntegration" {
rest_api_id = "${aws_api_gateway_rest_api.PyAPI.id}"
resource_id = "${aws_api_gateway_resource.ExecutePyResource.id}"
http_method = "${aws_api_gateway_method.ExecutePyMethod.http_method}"

integration_http_method = "POST"
type = "AWS"
uri = "${aws_lambda_function.PyLambdaFunction.invoke_arn}"
}

## Response mapping
resource "aws_api_gateway_method_response" "200" {
rest_api_id = "${aws_api_gateway_rest_api.PyAPI.id}"
resource_id = "${aws_api_gateway_resource.ExecutePyResource.id}"
http_method = "${aws_api_gateway_method.ExecutePyMethod.http_method}"
status_code = "200"
}

resource "aws_api_gateway_integration_response" "LambdaIntegrationResponse" {
rest_api_id = "${aws_api_gateway_rest_api.PyAPI.id}"
resource_id = "${aws_api_gateway_resource.ExecutePyResource.id}"
http_method = "${aws_api_gateway_method.ExecutePyMethod.http_method}"
status_code = "${aws_api_gateway_method_response.200.status_code}"
depends_on = ["aws_api_gateway_integration.LambdaIntegration"]
}

## API DEPLOYMENT
resource "aws_api_gateway_deployment" "PyAPIDeployment" {
depends_on = [
"aws_api_gateway_integration.LambdaIntegration"
]

rest_api_id = "${aws_api_gateway_rest_api.PyAPI.id}"
stage_name = "v1"
}

## Permission for API Gateway DEPLOYMENT to access Lambda
resource "aws_lambda_permission" "apigw" {
statement_id = "AllowAPIGatewayInvoke"
action = "lambda:InvokeFunction"
function_name = "${aws_lambda_function.PyLambdaFunction.arn}"
principal = "apigateway.amazonaws.com"

# The /*/* portion grants access from any method on any resource
# within the API Gateway "REST API".
source_arn = "${aws_api_gateway_deployment.PyAPIDeployment.execution_arn}/*/*"
}

## Permission for API Gateway REST API to access Lambda
resource "aws_lambda_permission" "lambda_permission" {
statement_id = "AllowTfServerlessPythonAPIInvoke"
action = "lambda:InvokeFunction"
function_name = "${aws_lambda_function.PyLambdaFunction.arn}"
principal = "apigateway.amazonaws.com"

# The /*/*/* part allows invocation from any stage, method and resource path
# within API Gateway REST API.
source_arn = "${aws_api_gateway_rest_api.PyAPI.execution_arn}/*/*/*"
}

output "base_url" {
value = "${aws_api_gateway_deployment.PyAPIDeployment.invoke_url}"
}
51 changes: 51 additions & 0 deletions terraform/lambda.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
##
## based on an excellent terrform for lambda example:
## https://www.terraform.io/docs/providers/aws/guides/serverless-with-aws-lambda-and-api-gateway.html
##


provider "aws" {
region = "eu-central-1"
}

variable "app_version" {
}

resource "aws_lambda_function" "PyLambdaFunction" {
function_name = "PythonEvalLambda"

# The bucket name as created earlier with "aws s3api create-bucket"
s3_bucket = "terraform-serverless-python"
s3_key = "v${var.app_version}/python.zip"

# "main" is the filename within the zip file (main.py) and "handler"
# is the name of the property under which the handler function was
# exported in that file.
handler = "lambda.lambda_handler"
runtime = "python3.6"

role = "${aws_iam_role.lambda_exec.arn}"
}


# IAM role which dictates what other AWS services the Lambda function
# may access.
resource "aws_iam_role" "lambda_exec" {
name = "serverless_lambda_python"

assume_role_policy = <<EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Action": "sts:AssumeRole",
"Principal": {
"Service": "lambda.amazonaws.com"
},
"Effect": "Allow",
"Sid": ""
}
]
}
EOF
}