🚀Terraform: Serverless Application using AWS Lambda and API Gateway

>>> Setting up a serverless architecture to deploy and manage Lambda functions with API Gateway endpoints using Terraform configuration.

Sai Manasa
7 min readSep 15, 2024
Sererless Application: Terraform by Sai Manasa

Hello World… In today’s cloud-native world, deploying serverless applications has become increasingly popular due to their scalability and cost-effectiveness. AWS Lambda, combined with API Gateway, offers a powerful way to build and manage serverless functions and expose them via HTTP endpoints. In this blog, we’ll walk through how to use Terraform to set up a basic serverless application on AWS, covering the deployment of a simple “Hello World” Lambda function and configuring API Gateway to trigger it. Whether you’re new to serverless architectures or looking to refine your infrastructure as code skills, this guide will provide clear, step-by-step instructions to get you started. So, let’s get started…

Task Overview:

  • We will set up a basic serverless application using AWS Lambda and API Gateway in this task.
  • We aim to create a Lambda function that responds to HTTP requests, which will be exposed via an API Gateway endpoint.
  • We’ll use Terraform to automate the deployment of these resources, including setting up the necessary permissions and configuring the API Gateway to route requests to our Lambda function.
  • By the end of this task, we will have a fully operational serverless endpoint that can be accessed over the Internet.

Hands-On Guide:

Step 1: Install Terraform

  • Install Terraform: here

Step 2: Lambda Function

  • Create a Python file for the Lambda function hello_world.py:
  • Package the lambda function into a zip file:
zip lambda.zip hello_world.py

Step 3: Provider Configuration

# Provider Configuration

provider "aws" {
region = "us-east-1"
}
  • provider "aws": Specifies the provider that is being used, here it is AWS.
  • region = "us-east-1": Sets the AWS region where the resources will be created.

Step 4: IAM Role For Lambda

# IAM role for Lambda

resource "aws_iam_role" "lambda_exec_role" {
name = "lambda_exec_role"

assume_role_policy = <<EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Action": "sts:AssumeRole",
"Effect": "Allow",
"Principal": {
"Service": "lambda.amazonaws.com"
}
}
]
}
EOF
}
  • resource "aws_iam_role" "lambda_exec_role": Defines an IAM role named lambda_exec_role for Lambda.
  • assume_role_policy: Specifies the policy that allows AWS Lambda to assume this role. It defines that the Lambda service can assume the role (lambda.amazonaws.com).

Step 5: IAM Policy For Lambda Execution

# IAM policy for Lambda execution

resource "aws_iam_role_policy" "lambda_exec_policy" {
role = aws_iam_role.lambda_exec_role.id
policy = <<EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"logs:CreateLogGroup",
"logs:CreateLogStream",
"logs:PutLogEvents"
],
"Resource": "*"
}
]
}
EOF
}
  • resource "aws_iam_role_policy" "lambda_exec_policy": Attaches a policy to the IAM role created above.
  • role = aws_iam_role.lambda_exec_role.id: Associates this policy with the IAM role created earlier.
  • policy: Defines permissions for the Lambda function. In this case, it allows creating and managing logs in CloudWatch (logs:CreateLogGroup, logs:CreateLogStream, logs:PutLogEvents).

Step 6: Lambda Function

# Lambda function

resource "aws_lambda_function" "hello_world" {
filename = "lambda.zip"
function_name = "hello_world_function"
role = aws_iam_role.lambda_exec_role.arn
handler = "hello_world.lambda_handler"
runtime = "python3.9"

source_code_hash = filebase64sha256("lambda.zip")
}
  • resource "aws_lambda_function" "hello_world": Defines a Lambda function named hello_world_function.
  • filename = "lambda.zip": Specifies the path to the ZIP file containing the Lambda code.
  • function_name = "hello_world_function": Sets the name of the Lambda function.
  • role = aws_iam_role.lambda_exec_role.arn: Grants the Lambda function permissions using the IAM role created above.
  • handler = "hello_world.lambda_handler": Specifies the file and method that AWS Lambda will invoke. hello_world is the filename and lambda_handler is the function inside that file.
  • runtime = "python3.9": Defines the runtime environment for the Lambda function.
  • source_code_hash: Provides a base64-encoded SHA256 hash of the ZIP file, which helps Terraform track changes to the code.

Step 7: API Gateway REST API

# API Gateway REST API

resource "aws_api_gateway_rest_api" "hello_world_api" {
name = "HelloWorldAPI"
description = "API for Hello World Lambda function"
}
  • resource "aws_api_gateway_rest_api" "hello_world_api": Defines an API Gateway REST API.
  • name = "HelloWorldAPI": Sets the name of the API.
  • description = "API for Hello World Lambda function": Describes the API.

Step 8: API Gateway Resource (Endpoint)

# API Gateway Resource (Endpoint)

resource "aws_api_gateway_resource" "lambda_resource" {
rest_api_id = aws_api_gateway_rest_api.hello_world_api.id
parent_id = aws_api_gateway_rest_api.hello_world_api.root_resource_id
path_part = "hello"
}
  • resource "aws_api_gateway_resource" "lambda_resource": Defines a resource (endpoint) in the API Gateway.
  • rest_api_id = aws_api_gateway_rest_api.hello_world_api.id: Associates this resource with the API created above.
  • parent_id = aws_api_gateway_rest_api.hello_world_api.root_resource_id: Specifies that this resource is a child of the root resource of the API.
  • path_part = "hello": Sets the path for this resource (endpoint) to /hello.

Step 9: API Gateway Method (GET Request)

# API Gateway Method (GET request)

resource "aws_api_gateway_method" "get_method" {
rest_api_id = aws_api_gateway_rest_api.hello_world_api.id
resource_id = aws_api_gateway_resource.lambda_resource.id
http_method = "GET"
authorization = "NONE"
}
  • resource "aws_api_gateway_method" "get_method": Defines an HTTP method for the API Gateway resource.
  • rest_api_id = aws_api_gateway_rest_api.hello_world_api.id: Associates this method with the API.
  • resource_id = aws_api_gateway_resource.lambda_resource.id: Associates this method with the resource (/hello).
  • http_method = "GET": Specifies the HTTP method (GET) for this endpoint.
  • authorization = "NONE": Indicates that no authorization is required for this method.

Step 10: Lambda Integration for API Gateway

# Lambda integration for API Gateway

resource "aws_api_gateway_integration" "lambda_integration" {
rest_api_id = aws_api_gateway_rest_api.hello_world_api.id
resource_id = aws_api_gateway_resource.lambda_resource.id
http_method = aws_api_gateway_method.get_method.http_method
type = "AWS"
integration_http_method = "POST"
uri = aws_lambda_function.hello_world.invoke_arn
}
  • resource "aws_api_gateway_integration" "lambda_integration": Defines the integration between API Gateway and Lambda.
  • rest_api_id = aws_api_gateway_rest_api.hello_world_api.id: Associates this integration with the API.
  • resource_id = aws_api_gateway_resource.lambda_resource.id: Associates this integration with the resource (/hello).
  • http_method = aws_api_gateway_method.get_method.http_method: Specifies the HTTP method (GET) for this integration.
  • type = "AWS": Specifies that this integration is with an AWS service (Lambda).
  • integration_http_method = "POST": Specifies the HTTP method used to call the Lambda function.
  • uri = aws_lambda_function.hello_world.invoke_arn: Provides the ARN of the Lambda function for integration.

Step 11: Lambda Permission for API Gateway

# Lambda permission to allow API Gateway to invoke the function

resource "aws_lambda_permission" "allow_api_gateway" {
statement_id = "AllowExecutionFromApiGateway"
action = "lambda:InvokeFunction"
function_name = aws_lambda_function.hello_world.function_name
principal = "apigateway.amazonaws.com"
source_arn = "${aws_api_gateway_rest_api.hello_world_api.execution_arn}/*/*"
}
  • resource "aws_lambda_permission" "allow_api_gateway": Grants API Gateway permission to invoke the Lambda function.
  • statement_id = "AllowExecutionFromApiGateway": A unique identifier for the permission statement.
  • action = "lambda:InvokeFunction": Specifies the action allowed (invoking the Lambda function).
  • function_name = aws_lambda_function.hello_world.function_name: Specifies the Lambda function name.
  • principal = "apigateway.amazonaws.com": Specifies that API Gateway is the principal allowed to invoke the Lambda function.
  • source_arn = "${aws_api_gateway_rest_api.hello_world_api.execution_arn}/*/*": Specifies the ARN of the API Gateway execution that is allowed to invoke the Lambda function.

Step 12: Deploy API Gateway

# Deploy API Gateway

resource "aws_api_gateway_deployment" "api_deployment" {
depends_on = [aws_api_gateway_integration.lambda_integration]
rest_api_id = aws_api_gateway_rest_api.hello_world_api.id
stage_name = "prod"
}
  • resource "aws_api_gateway_deployment" "api_deployment": Defines the deployment of the API Gateway.
  • depends_on = [aws_api_gateway_integration.lambda_integration]: Ensures that the deployment happens after the integration is set up.
  • rest_api_id = aws_api_gateway_rest_api.hello_world_api.id: Associates this deployment with the API.
  • stage_name = "prod": Specifies the stage name for this deployment. Here, it is prod.

Step 13: Output API Gateway URL

# Output API Gateway URL

output "api_url" {
value = "${aws_api_gateway_deployment.api_deployment.invoke_url}/hello"
}
  • This output block provides the URL for the API Gateway endpoint.
  • It concatenates the base URL of the API Gateway deployment with the /hello path to give the full URL where we can access the Lambda function via HTTP GET requests.

Step 14: Final Config File

Step 15: Initialize Terraform

  • Download the necessary providers.
terraform init

Step 16: Plan the Configuration

terraform plan

Step 17: Apply the configuration

terraform apply # Review the proposed changes and type 'yes' to confirm.

terraform apply -auto-approve

.

.

.

Step 18: Testing

  • We can get the output URL from Terraform. It will be displayed after the apply command completes, or you can check it with:
terraform output api_url
  • Test the API using curl or any HTTP client:
curl <API_URL>

Step 20: Results

Step 21: Clean Up

To delete the above-deployed application, use the destroy command:

terraform destroy # Review the proposed changes and type 'yes' to confirm.
terraform destroy -auto-approve

File Structure:

Here’s the file structure for your serverless application using AWS Lambda and API Gateway with Terraform:

Source Code:

Step into my GitHub Repo, where I’ve compiled a comprehensive collection of source code.

Let’s Connect:

Feel free to reach out, share your thoughts, or ask any questions you may have. I’m excited to engage with you and learn from each other as we navigate this exciting field!

LinkedIn: Sai Manasa

GitHub: Sai Manasa

Happy Infrastructure-ing 💻

Happy Learning 😄

--

--