Skip to content

Commit

Permalink
Lambda-backed REST and HTTP apis
Browse files Browse the repository at this point in the history
  • Loading branch information
ajhool authored and stack72 committed May 7, 2021
1 parent 4be0c05 commit cd21369
Show file tree
Hide file tree
Showing 7 changed files with 247 additions and 0 deletions.
2 changes: 2 additions & 0 deletions aws-py-apigateway-lambda-serverless/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
*.pyc
venv/
6 changes: 6 additions & 0 deletions aws-py-apigateway-lambda-serverless/Pulumi.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
name: aws-py-apigateway-lambda-serverless
runtime:
name: python
options:
virtualenv: venv
description: A minimal AWS Python Pulumi program
68 changes: 68 additions & 0 deletions aws-py-apigateway-lambda-serverless/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
# [App Description] using [Service or Tool]

<!-- Use Title Case for all Titles -->
<!-- Most of the examples are transformed into tutorials on https://www.pulumi.com/docs/tutorials/ and are sorted by cloud and language. There is no need to include the cloud provider name or the language in the title.
<!-- Our examples have a specific structure. Learn more at CONTRIBUTING.md -->

Set up two lambda-backed API Gateways: an API Gateway V1 (REST) and an API Gateway V2 (HTTP). AWS provides some information on the differences between these two API Gateway types, here: (Announcing HTTP APIs for Amazon API Gateway)[https://aws.amazon.com/blogs/compute/announcing-http-apis-for-amazon-api-gateway/] & (API Gateway V2 FAQs)[https://aws.amazon.com/api-gateway/faqs/]

# Lambda-backed API Gateway

This example provides API endpoints which are executed by lambda using TypeScript and AWS.

This sample uses the following AWS products:

- [Amazon API Gateway](https://aws.amazon.com/api-gateway/) is used as an API proxy
- [AWS Lambda](https://aws.amazon.com/lambda/) is used to process API events by executing typescript/javascript code

## Prerequisites

<!-- The Prerequisites section includes an ordered list of required installation and configuration steps before the reader can deploy the Pulumi example. -->

1. [Install Pulumi](https://www.pulumi.com/docs/get-started/install/)
2. Create a new stack:

```bash
$ pulumi stack init aws-py-apigateway-lambda-serverless
```

3. Set the AWS region:

```bash
$ pulumi config set aws:region us-east-2
```

## Deploy the App

1. Run `pulumi up` to preview and deploy changes:

2. To view the runtime logs of the Lambda function, use the `pulumi logs` command. To get a log stream, use `pulumi logs --follow`.

## Clean Up

1. Run `pulumi destroy` to tear down all resources.

2. To delete the stack itself, run `pulumi stack rm`. Note that this command deletes all deployment history from the Pulumi Console.

<!-- Example:
1. [Install Pulumi](https://www.pulumi.com/docs/get-started/install/)
1. [Configure your AWS Credentials](https://www.pulumi.com/docs/intro/cloud-providers/aws/setup/)
1. [Install Node.js](https://www.pulumi.com/docs/intro/languages/javascript/)
-->

## Summary

In this tutorial, you built a lambda-backed API on AWS using API Gateway, lambda functions, and Pulumi. This serverless solution is highly scaleable, resilient, and stateless.

<!-- Give a quick recap of what the readers have learned and optionally provide places for further exploration. -->

## Next Steps

- [Create a frontend to interact with this api](https://www.pulumi.com/docs/tutorials/aws/s3-website/)
<!-- Optionally include an unordered list of relevant Pulumi tutorials. -->

<!-- Example:
- [Create a load-balanced, hosted NGINX container service](https://www.pulumi.com/docs/tutorials/aws/ecs-fargate/)
- [Create an EC2-based WebServer and associated infrastructure](https://www.pulumi.com/docs/tutorials/aws/ec2-webserver/)
-->
127 changes: 127 additions & 0 deletions aws-py-apigateway-lambda-serverless/__main__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
"""An AWS Python Pulumi program"""

import pulumi
import pulumi_aws as aws
import iam
import json

region = aws.config.region

custom_stage_name = 'example'

##################
## Lambda Function
##################

# Create a Lambda function, using code from the `./app` folder.

lambda_func = aws.lambda_.Function("mylambda",
role=iam.lambda_role.arn,
runtime="python3.7",
handler="hello.handler",
code=pulumi.AssetArchive({
'.': pulumi.FileArchive('./hello_lambda')
})
)


####################################################################
##
## API Gateway REST API (API Gateway V1 / original)
## /{proxy+} - passes all requests through to the lambda function
##
####################################################################

# Create a single Swagger spec route handler for a Lambda function.
def swagger_route_handler(arn):
return ({
"x-amazon-apigateway-any-method": {
"x-amazon-apigateway-integration": {
"uri": f'arn:aws:apigateway:{region}:lambda:path/2015-03-31/functions/{arn}/invocations',
"passthroughBehavior": "when_no_match",
"httpMethod": "POST",
"type": "aws_proxy",
},
},
})

# Create the API Gateway Rest API, using a swagger spec.
rest_api = aws.apigateway.RestApi("api",
body=lambda_func.arn.apply(lambda arn: json.dumps({
"swagger": "2.0",
"info": {"title": "api", "version": "1.0"},
"paths": {
"/{proxy+}": swagger_route_handler(arn),
},
})))

# Create a deployment of the Rest API.
deployment = aws.apigateway.Deployment("api-deployment",
rest_api=rest_api.id,
# Note: Set to empty to avoid creating an implicit stage, we'll create it
# explicitly below instead.
stage_name="",
)

# Create a stage, which is an addressable instance of the Rest API. Set it to point at the latest deployment.
stage = aws.apigateway.Stage("api-stage",
rest_api=rest_api.id,
deployment=deployment.id,
stage_name=custom_stage_name,
)

# Give permissions from API Gateway to invoke the Lambda
invoke_permission = aws.lambda_.Permission("api-lambda-permission",
action="lambda:invokeFunction",
function=lambda_func.name,
principal="apigateway.amazonaws.com",
source_arn=deployment.execution_arn.apply(lambda arn: arn + "*/*"),
)

#########################################################################
# Create an HTTP API and attach the lambda function to it
#########################################################################

http_endpoint = aws.apigatewayv2.Api("http-api-pulumi-example",
protocol_type="HTTP"
)

# Note to self: example provided her ew as incorrect: https://www.pulumi.com/docs/reference/pkg/aws/apigatewayv2/integration/
http_lambda_backend = aws.apigatewayv2.Integration("example",
api_id= http_endpoint.id,
integration_type="AWS_PROXY",
connection_type="INTERNET",
description="Lambda example",
integration_method="POST",
integration_uri=lambda_func.arn,
passthrough_behavior="WHEN_NO_MATCH"
)

url = http_lambda_backend.integration_uri.apply(lambda l: "" + l)

http_route = aws.apigatewayv2.Route("example-route",
api_id=http_endpoint.id,
route_key="ANY /example-route/{proxy+}",
target=http_lambda_backend.id.apply(lambda targetUrl: "integrations/" + targetUrl)
)

http_stage = aws.apigatewayv2.Stage("example-stage",
api_id=http_endpoint.id,
route_settings= [
{
"route_key": http_route.route_key.apply(lambda route_key: "" + route_key)
}
],
auto_deploy=True
)

# http_deployment = aws.apigatewayv2.Deployment("example-deployment",
# api_id=http_endpoint.id,
# description="Example deployment"
# )

# pulumi.export("http-endpoint", http_endpoint.api_endpoint)
# Export the https endpoint of the running Rest API
pulumi.export("apigateway-rest-endpoint", deployment.invoke_url.apply(lambda url: url + custom_stage_name))
# See "Outputs" for (Inputs and Outputs)[https://www.pulumi.com/docs/intro/concepts/inputs-outputs/] the usage of the pulumi.Output.all function to do string concatenation
pulumi.export("apigatewayv2-http-endpoint", pulumi.Output.all(http_endpoint.api_endpoint, http_stage.name).apply(lambda values: values[0] + '/' + values[1]))
7 changes: 7 additions & 0 deletions aws-py-apigateway-lambda-serverless/hello_lambda/hello.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import json

def handler(event, context):
return {
"statusCode": 200,
"body": json.dumps('Cheers from AWS Lambda!!')
}
35 changes: 35 additions & 0 deletions aws-py-apigateway-lambda-serverless/iam.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# Copyright 2016-2018, Pulumi Corporation. All rights reserved.

from pulumi_aws import iam

lambda_role = iam.Role('lambdaRole',
assume_role_policy="""{
"Version": "2012-10-17",
"Statement": [
{
"Action": "sts:AssumeRole",
"Principal": {
"Service": "lambda.amazonaws.com"
},
"Effect": "Allow",
"Sid": ""
}
]
}"""
)

lambda_role_policy = iam.RolePolicy('lambdaRolePolicy',
role=lambda_role.id,
policy="""{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Action": [
"logs:CreateLogGroup",
"logs:CreateLogStream",
"logs:PutLogEvents"
],
"Resource": "arn:aws:logs:*:*:*"
}]
}"""
)
2 changes: 2 additions & 0 deletions aws-py-apigateway-lambda-serverless/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
pulumi>=2.0.0,<3.0.0
pulumi-aws>=3.2.0,<4.0.0

0 comments on commit cd21369

Please sign in to comment.