Skip to content

Commit

Permalink
Merge pull request #2 from futurice/remove-cloudfront-from-aws-lambda…
Browse files Browse the repository at this point in the history
…-api

Remove CloudFront from aws_lambda_api
  • Loading branch information
jareware authored Apr 11, 2019
2 parents eae9573 + 3a0da9a commit a0b02cf
Show file tree
Hide file tree
Showing 8 changed files with 167 additions and 332 deletions.
72 changes: 38 additions & 34 deletions aws_lambda_api/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,12 @@
This module creates a Lambda function, and makes it available via a custom domain, complete with SSL termination: e.g. `https://api.example.com/`. This includes:

- DNS records on [Route 53](https://aws.amazon.com/route53/)
- A [CloudFront](https://aws.amazon.com/cloudfront/) distribution for SSL termination
- An SSL certificate for the distribution from [ACM](https://aws.amazon.com/certificate-manager/)
- A [Lambda](https://aws.amazon.com/lambda/) function built from your JavaScript code
- An SSL certificate for the domain from [ACM](https://aws.amazon.com/certificate-manager/)
- [API Gateway](https://aws.amazon.com/api-gateway/) configuration for invoking the function over HTTP
- A [Lambda](https://aws.amazon.com/lambda/) function built from your JavaScript code

## Example 1: Simple API

**Important:** CloudFront operations are generally very slow. Your `terraform apply` may take anywhere **from 10 minutes up to 45 minutes** to complete.

First, write down some simple code to deploy in a file called `index.js`:

```js
Expand All @@ -31,15 +28,6 @@ exports.handler = function(event, context, callback) {
Assuming you have the [AWS provider](https://www.terraform.io/docs/providers/aws/index.html) set up, and a DNS zone for `example.com` configured on Route 53:

```tf
# Several AWS services (such as ACM & Lambda@Edge) are presently only available in the US East region.
# To be able to use them, we need a separate AWS provider for that region, which can be used with an alias.
# Make sure you customize this block to match your regular AWS provider configuration.
# https://www.terraform.io/docs/configuration/providers.html#multiple-provider-instances
provider "aws" {
alias = "us_east_1"
region = "us-east-1"
}
# Lambda functions can only be uploaded as ZIP files, so we need to package our JS file into one
data "archive_file" "lambda_zip" {
type = "zip"
Expand All @@ -62,7 +50,7 @@ module "my_api" {
}
```

After `terraform apply` (which may take a **very** long time), you should be able to visit `https://api.example.com/`, and be greeted by the above `Hello World!` message.
After `terraform apply`, you should be able to visit `https://api.example.com/`, and be greeted by the above `Hello World!` message.

Because we included the `lambda_logging_enabled` option, you can also log into CloudWatch and check out the properties Lambda makes available in the `event` and `context` properties.

Expand All @@ -83,15 +71,6 @@ Importantly, the most recent compiled version of the Lambda function should alwa
Assuming you have the [AWS provider](https://www.terraform.io/docs/providers/aws/index.html) set up, and a DNS zone for `example.com` configured on Route 53:

```tf
# Several AWS services (such as ACM & Lambda@Edge) are presently only available in the US East region.
# To be able to use them, we need a separate AWS provider for that region, which can be used with an alias.
# Make sure you customize this block to match your regular AWS provider configuration.
# https://www.terraform.io/docs/configuration/providers.html#multiple-provider-instances
provider "aws" {
alias = "us_east_1"
region = "us-east-1"
}
module "my_api" {
# Available inputs: https://github.com/futurice/terraform-utils/tree/master/aws_lambda_api#inputs
# Check for updates: https://github.com/futurice/terraform-utils/compare/v9.4...master
Expand All @@ -103,7 +82,7 @@ module "my_api" {
}
```

After `terraform apply` (which may take a **very** long time), you should be able to receive a random joke with:
After `terraform apply`, you should be able to receive a random joke with:

```bash
$ curl https://api.example.com
Expand All @@ -130,15 +109,6 @@ Bundling the code and build artifacts for your Lambda function is all well and g
This also makes it easy to support multiple environments, and release promotions between them. For example:

```tf
# Several AWS services (such as ACM & Lambda@Edge) are presently only available in the US East region.
# To be able to use them, we need a separate AWS provider for that region, which can be used with an alias.
# Make sure you customize this block to match your regular AWS provider configuration.
# https://www.terraform.io/docs/configuration/providers.html#multiple-provider-instances
provider "aws" {
alias = "us_east_1"
region = "us-east-1"
}
resource "aws_s3_bucket" "my_builds" {
bucket = "my-builds"
}
Expand Down Expand Up @@ -250,6 +220,40 @@ EOF

Otherwise API Gateway won't have permission to write logs to CloudWatch.

## Supporting CORS

Your API can easily support CORS, if needed. For example:

```js
// https://enable-cors.org/server_nginx.html
const CORS_HEADERS = {
"Access-Control-Allow-Origin": "*",
"Access-Control-Allow-Methods": "POST,OPTIONS,GET,PUT,PATCH,DELETE",
"Access-Control-Allow-Headers": "DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range",
"Access-Control-Expose-Headers": "Content-Length,Content-Range",
};

exports.handler = function(event, context, callback) {
console.log("Lambda function event:", event);
console.log("Lambda function context:", context);
if (event.httpMethod === "OPTIONS") { // this is (probably) a CORS preflight request
callback(null, {
statusCode: 200,
headers: CORS_HEADERS,
});
} else { // this is a regular request
callback(null, {
statusCode: 200,
headers: {
...CORS_HEADERS,
"Content-Type": "application/json"
},
body: JSON.stringify({ Hello: "World!" }),
});
}
};
```

<!-- terraform-docs:begin -->
## Inputs

Expand Down
155 changes: 0 additions & 155 deletions aws_lambda_api/api_gateway.tf

This file was deleted.

50 changes: 50 additions & 0 deletions aws_lambda_api/api_gateway_config.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
resource "aws_api_gateway_rest_api" "this" {
name = "${local.prefix_with_domain}"
description = "${var.comment_prefix}${var.api_domain}"
}

resource "aws_api_gateway_deployment" "this" {
rest_api_id = "${aws_api_gateway_rest_api.this.id}"

depends_on = [
"aws_api_gateway_integration.proxy_root",
"aws_api_gateway_integration.proxy_other",
]
}

resource "aws_api_gateway_stage" "this" {
stage_name = "${var.stage_name}"
description = "${var.comment_prefix}${var.api_domain}"
rest_api_id = "${aws_api_gateway_rest_api.this.id}"
deployment_id = "${aws_api_gateway_deployment.this.id}"
tags = "${var.tags}"
}

resource "aws_api_gateway_method_settings" "this" {
rest_api_id = "${aws_api_gateway_rest_api.this.id}"
stage_name = "${aws_api_gateway_stage.this.stage_name}"
method_path = "*/*"

settings {
metrics_enabled = "${var.api_gateway_cloudwatch_metrics}"
logging_level = "${var.api_gateway_logging_level}"
data_trace_enabled = "${var.api_gateway_logging_level == "OFF" ? false : true}"
throttling_rate_limit = "${var.throttling_rate_limit}"
throttling_burst_limit = "${var.throttling_burst_limit}"
}
}

resource "aws_api_gateway_domain_name" "this" {
domain_name = "${var.api_domain}"
regional_certificate_arn = "${aws_acm_certificate_validation.this.certificate_arn}"

endpoint_configuration {
types = ["REGIONAL"]
}
}

resource "aws_api_gateway_base_path_mapping" "this" {
api_id = "${aws_api_gateway_rest_api.this.id}"
stage_name = "${aws_api_gateway_stage.this.stage_name}"
domain_name = "${aws_api_gateway_domain_name.this.domain_name}"
}
64 changes: 64 additions & 0 deletions aws_lambda_api/api_gateway_resources.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
# Add root resource to the API (it it needs to be included separately from the "proxy" resource defined below), which forwards to our Lambda:

resource "aws_api_gateway_method" "proxy_root" {
rest_api_id = "${aws_api_gateway_rest_api.this.id}"
resource_id = "${aws_api_gateway_rest_api.this.root_resource_id}"
http_method = "ANY"
authorization = "NONE"
}

resource "aws_api_gateway_integration" "proxy_root" {
rest_api_id = "${aws_api_gateway_rest_api.this.id}"
resource_id = "${aws_api_gateway_method.proxy_root.resource_id}"
http_method = "${aws_api_gateway_method.proxy_root.http_method}"
integration_http_method = "POST"
type = "AWS_PROXY"
uri = "${local.function_invoke_arn}"
}

# Add a "proxy" resource, that matches all paths (except the root, defined above) and forwards them to our Lambda:

resource "aws_api_gateway_resource" "proxy_other" {
rest_api_id = "${aws_api_gateway_rest_api.this.id}"
parent_id = "${aws_api_gateway_rest_api.this.root_resource_id}"
path_part = "{proxy+}"
}

resource "aws_api_gateway_method" "proxy_other" {
rest_api_id = "${aws_api_gateway_rest_api.this.id}"
resource_id = "${aws_api_gateway_resource.proxy_other.id}"
http_method = "ANY"
authorization = "NONE"
}

resource "aws_api_gateway_integration" "proxy_other" {
rest_api_id = "${aws_api_gateway_rest_api.this.id}"
resource_id = "${aws_api_gateway_method.proxy_other.resource_id}"
http_method = "${aws_api_gateway_method.proxy_other.http_method}"
integration_http_method = "POST"
type = "AWS_PROXY"
uri = "${local.function_invoke_arn}"
}

resource "aws_api_gateway_method_response" "proxy_other" {
rest_api_id = "${aws_api_gateway_rest_api.this.id}"
resource_id = "${aws_api_gateway_resource.proxy_other.id}"
http_method = "${aws_api_gateway_method.proxy_other.http_method}"
status_code = "200"

response_models = {
"application/json" = "Empty"
}
}

resource "aws_api_gateway_integration_response" "proxy_other" {
depends_on = ["aws_api_gateway_integration.proxy_other"]
rest_api_id = "${aws_api_gateway_rest_api.this.id}"
resource_id = "${aws_api_gateway_resource.proxy_other.id}"
http_method = "${aws_api_gateway_method.proxy_other.http_method}"
status_code = "${aws_api_gateway_method_response.proxy_other.status_code}"

response_templates = {
"application/json" = ""
}
}
Loading

0 comments on commit a0b02cf

Please sign in to comment.