API Gateway HTTP to Lambda with Modules

Sign Up to Build

About this Architecture

Here is some information about this architecture.

This solution creates an API Gateway HTTP API that connects to a Lambda Function. This solution is built using Terraform public registry modules.

How to Build This Solution

Here are the steps you can follow to build this solution on your own.

Get Your AWS Credentials

If you're using the Skillmix Labs feature, open the lab settings (the beaker icon) on the right side of the code editor. Then, click the Start Lab button to start hte lab environment.

Wait for the credentials to load. Then run this in the terminal:

$ aws configure --profile smx-lab
AWS Access Key ID [None]: AKIA3E3W34P42CSHXDH5
AWS Secret Access Key [None]: vTmqpOqefgJfse8i6QwzgpjgswPjHZ6h/oiQq4zf
Default region name [None]: us-west-2
Default output format [None]: json

Be sure to name your credentials profile 'smx-lab'.

Note: If you're using your own AWS account you'll need to ensure that you've created and configured a named AWS CLI profile named smx-lab.

Create the Terraform File

We'll be doing all of our work in one Terraform file. Create a new directory on your computer somewhere, and then create a file named main.tf in it.

Create the Terraform & Provider Block

Next, we will create a Terraform configuration that will allow us to use the AWS provider. This configuration will require us to specify the version of the AWS provider that we want to use, as well as the version of Terraform that we are using. We will also specify the AWS profile and region that we want to use. This code will ensure that the correct versions of Terraform and the AWS provider are used, and that the AWS provider is configured correctly.

Append this code to the main.tf file:

terraform {
  required_version = ">= 1.0"

  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = ">= 4.9"
    }
    random = {
      source  = "hashicorp/random"
      version = ">= 2.0"
    }
  }
}


provider "aws" {
  profile = "smx-temp"
  region = "us-west-2"
}

Create the Data Resource Blocks

Next, we will create a data source that will allow us to access the AWS caller identity of the current user. This data source will be called "aws_caller_identity" and will be set to the "current" provider, which is the AWS Cross Account provider. This data source will allow us to access the identity of the current user, which can be used for authentication and authorization purposes.

Append this code to the main.tf file:

data "aws_caller_identity" "current" {}

data "aws_region" "current" {}

Create a Python File

We will need a Python function to execute via Lambda. In your working directory, create a folder named src, and in that folder create a file named index.py. Add the following code to that python file:

import json
import logging

logger = logging.getLogger()
logger.setLevel(logging.INFO)


def lambda_handler(event, context):
    logging.info(json.dumps(event, indent=2))

    # [ERROR] TypeError: Object of type LambdaContext is not JSON serializable
    # logging.info(json.dumps(context, indent=2))

    eventObject = {
        "hello": "Hello Python! Hello Terraform!",
        "functionName": context.function_name,
        "event": event
    }

    return {
        "statusCode": 200,
        "headers": {
            "Content-Type": "application/json"
        },
        "body": json.dumps(eventObject)
    }

Create the Lambda Function via a Module

Next, we will create a Lambda function using the Terraform module "terraform-aws-modules/lambda/aws". This module will create a Lambda function with the name "${random_pet.this.id}-lambda", a description of "My awesome lambda function", a handler of "index.lambda_handler", a runtime of "python3.8", and a source path of "${path.module}/src". Additionally, this module will allow the Lambda function to be triggered from an API Gateway, and will add two tags to the Lambda function.

Append this code to the main.tf file:

module "lambda_function" {
  source  = "terraform-aws-modules/lambda/aws"
  version = "~> 4.0"

  function_name = "${random_pet.this.id}-lambda"
  description   = "My awesome lambda function"
  handler       = "index.lambda_handler"
  runtime       = "python3.8"
  publish       = true

  source_path = "${path.module}/src"

  allowed_triggers = {
    AllowExecutionFromAPIGateway = {
      service    = "apigateway"
      source_arn = "${module.api_gateway.apigatewayv2_api_execution_arn}/*/*"
    }
  }

  tags = {
    Pattern = "terraform-apigw-http-lambda"
    Module  = "lambda_function"
  }
}

Create an API Gateway Resource

Next, we will create an API Gateway using the Terraform module "terraform-aws-modules/apigateway-v2/aws". This module will create an HTTP API Gateway with a name and description specified by the user. We will also configure CORS settings and an integration with a Lambda function. Finally, we will add tags to the API Gateway for easy identification.

Append this code to the main.tf file:

module "api_gateway" {
  source  = "terraform-aws-modules/apigateway-v2/aws"
  version = "~> 2.0"

  name          = "${random_pet.this.id}-http"
  description   = "My awesome HTTP API Gateway"
  protocol_type = "HTTP"

  create_api_domain_name = false

  cors_configuration = {
    allow_headers = ["content-type", "x-amz-date", "authorization", "x-api-key", "x-amz-security-token", "x-amz-user-agent"]
    allow_methods = ["*"]
    allow_origins = ["*"]
  }

  integrations = {
    "ANY /" = {
      lambda_arn             = module.lambda_function.lambda_function_arn
      payload_format_version = "2.0"
    }
  }

  tags = {
    Pattern = "terraform-apigw-http-lambda"
    Module  = "api_gateway"
  }
}

Create a Random "Pet" Name

Next, we will create a random pet resource using Terraform. This code will generate a random pet name with two words. The random pet resource will be named "this" and the length of the pet name will be two words.

Append this code to the main.tf file:

resource "random_pet" "this" {
  length = 2
}

Create Outputs

Let's create some project outputs. The Terraform code will output the ARN of the Lambda Function, the Invoke ARN of the Lambda Function, the name of the Lambda Function, the ARN identifying the Lambda Function Version, the latest published version of the Lambda Function, the ARN of the IAM role created for the Lambda Function, and the URI of the API

Append this code to the main.tf file:

# Lambda Function
output "lambda_function_arn" {
  description = "The ARN of the Lambda Function"
  value       = module.lambda_function.lambda_function_arn
}

output "lambda_function_invoke_arn" {
  description = "The Invoke ARN of the Lambda Function"
  value       = module.lambda_function.lambda_function_invoke_arn
}

output "lambda_function_name" {
  description = "The name of the Lambda Function"
  value       = module.lambda_function.lambda_function_name
}

output "lambda_function_qualified_arn" {
  description = "The ARN identifying your Lambda Function Version"
  value       = module.lambda_function.lambda_function_qualified_arn
}

output "lambda_function_version" {
  description = "Latest published version of Lambda Function"
  value       = module.lambda_function.lambda_function_version
}

# IAM Role of Lambda Function
output "lambda_role_arn" {
  description = "The ARN of the IAM role created for the Lambda Function"
  value       = module.lambda_function.lambda_role_arn
}

# API Gateway
output "apigatewayv2_api_api_endpoint" {
  description = "The URI of the API"
  value       = module.api_gateway.apigatewayv2_api_api_endpoint
}

Deploying the Project

Now that we have all of our code written, we can deploy the project. Open a terminal, navigate to the project, and run these commands.

# initialize the project 
$ terraform init 

# plan the project 
$ terraform plan 

# apply the project 
$ terraform apply

Test the Project

To test the project, call apigatewayv2_api_api_endpoint URL value that was outputted to your screen:

$ curl https://wargabe3ei.execute-api.eu-west-1.amazonaws.com

You should see some output from the Lambda function.

Destroy the Project

Run the destroy command to end it all

$ terraform destroy