Pumoxi

Disclaimer: This post’s content was generated with AI’s assistance.

In my previous blog post, I explored the benefits of serverless architecture using AWS Lambda and API Gateway. Now, let’s dive deeper and see how we can leverage Terraform to automate the deployment of this infrastructure.

Terraform is an Infrastructure as Code (IaC) tool that defines infrastructure resources (like Lambda functions and API Gateways) in a human-readable format. This blog post will guide you through a Terraform script that creates a complete serverless backend on AWS.

What We Will Build

This Terraform script will create the following resources:

  • IAM Role: A role with the necessary permissions for executing the Lambda function.
  • Lambda Function: A Python function deployed as a Lambda function.
  • API Gateway: An API Gateway endpoint that triggers your Lambda function.

Let’s Break Down the Code

The Terraform script can be divided into several sections:

1. Variables and Locals:

This section defines variables and locals used throughout the script. Variables like application and function_name allow you to customize your deployment. Locals, such as full_name, combine these variables to create unique names for your resources.

data "aws_region" "current" {}

data "aws_caller_identity" "current" {}

variable "application" {
    description = "Application Name"
    type = string
    default = "pumoxi"
}

variable "function_name" {
    description = "Name of the Function"
    type = string
    default = "blogpost20240820"
}

locals {
    full_name = "${var.application}-${var.function_name}"
    domain_name = "${var.application}.com"
}

2. IAM Role:

The aws_iam_role resource creates a role named after your application and function. It also defines a policy document data.aws_iam_policy_document that grants the role permission to assume itself (required by Lambda) and write logs to CloudWatch Logs.

resource "aws_iam_role" "iam_for_lambda" {
    name = local.full_name
    assume_role_policy = data.aws_iam_policy_document.assume_role.json

    tags = {
        Application = "${var.application}"
        Function = "${var.function_name}"
    }
}

data "aws_iam_policy_document" "assume_role" {
  statement {
    effect = "Allow"

    principals {
      type        = "Service"
      identifiers = ["lambda.amazonaws.com"]
    }

    actions = ["sts:AssumeRole"]
  }
}

resource "aws_iam_role_policy" "role_policy" {
  name = local.full_name
  role = aws_iam_role.iam_for_lambda.id

  policy = jsonencode (
    {
      Version = "2012-10-17"
      Statement =[
        {
          "Action": "logs:CreateLogGroup",
          "Resource": "arn:aws:logs:${data.aws_region.current.name}:${data.aws_caller_identity.current.account_id}:*",
          "Effect": "Allow"
        },
        {
          "Action": [
            "logs:CreateLogStream",
            "logs:PutLogEvents"
          ],
          "Resource": "arn:aws:logs:${data.aws_region.current.name}:${data.aws_caller_identity.current.account_id}:log-group:/aws/lambda/${local.full_name}:*",
          "Effect": "Allow"
        }
      ]
    }
  )
}

3. Lambda Function:

This section defines the aws_lambda_function resource. Here’s what’s happening:

  • function_name: Uses the local.full_name to create a unique name based on your application and function name.
  • handler: Specifies the function entry point within your Lambda code (lambda.main).
  • runtime: Defines the runtime environment for your function (python3.11).
  • filename: Points to the compressed Lambda code zip file generated by the data.archive_file resource.
  • role: Assigns the IAM role created earlier to grant the necessary permissions for the function.
  • source_code_hash: Ensures deployment updates when the Lambda code changes.
  • tags: Assign tags for easier resource management.
data "archive_file" "lambda" {
    type = "zip"
    source_file = "../lambda/lambda.py"
    output_path = "../lambda/lambda.zip"
}

resource "aws_lambda_function" "lambda" {
    function_name = local.full_name
    handler = "lambda.main"
    runtime = "python3.11"
    filename = "../lambda/lambda.zip"
    role = aws_iam_role.iam_for_lambda.arn

    source_code_hash = data.archive_file.lambda.output_base64sha256
    timeout = 300 # 1 minute

    tags = {
        Application = "${var.application}"
        Function = "${var.function_name}"
    }
}   

4. Lambda Permissions:

The aws_lambda_permission resource grants API Gateway permission to invoke your Lambda function.

resource "aws_lambda_permission" "apigw_lambda" {
    statement_id = "AllowExecutionFromAPIGateway"
    action = "lambda:InvokeFunction"
    function_name = aws_lambda_function.lambda.function_name
    principal = "apigateway.amazonaws.com"

}

5. API Gateway:

The aws_apigatewayv2_api resource creates an API Gateway with a name based on your application and function. It includes a description of the configuration of the protocol type.

resource "aws_apigatewayv2_api" "api" {
    name = local.full_name
    protocol_type = "HTTP"
    description = "API Gateway V2 for ${local.full_name}"

    tags = {
        Application = "${var.application}"
        Function = "${var.function_name}"
    }
}

6. API Gateway Stages and Deployment:

  • aws_apigatewayv2_stage: Defines a production stage (“prod”) for your API that points to the latest deployment.
  • aws_apigatewayv2_deployment: Creates a deployment for your API. It uses a trigger based on a hash of the involved resources (integration, routes) to ensure automatic redeployment when changes are made.
resource "aws_apigatewayv2_stage" "api" {
    api_id = aws_apigatewayv2_api.api.id
    name = "prod"
    deployment_id = aws_apigatewayv2_deployment.api.id
}

resource "aws_apigatewayv2_deployment" "api" {
    api_id = aws_apigatewayv2_api.api.id
    
    triggers = {
        redeployment = sha1(join(",",tolist([
jsonencode(aws_apigatewayv2_integration.api_integration),            jsonencode(aws_apigatewayv2_route.api_route_POST),
jsonencode(aws_apigatewayv2_route.api_route_OPTIONS),
])))
    }

    lifecycle {
      create_before_destroy = true
    }
}

7. API Gateway Integration and Routes:

  • aws_apigatewayv2_integration: Defines an integration between your API Gateway and your Lambda function. It uses the AWS Proxy integration type to pass incoming requests directly to your Lambda function.
  • aws_apigatewayv2_route: Creates two routes for your API:
    • A POST route for your specific function path (/ followed by the function_name).
    • An OPTIONS route to handle pre-flight CORS requests.
resource "aws_apigatewayv2_integration" "api_integration" {
    api_id = aws_apigatewayv2_api.api.id
    integration_type = "AWS_PROXY"
    integration_method = "POST"
    integration_uri = aws_lambda_function.lambda.invoke_arn
}

resource "aws_apigatewayv2_route" "api_route_POST" {
    api_id = aws_apigatewayv2_api.api.id
    route_key = "POST /${var.function_name}"
    target = "integrations/${aws_apigatewayv2_integration.api_integration.id}"
}

resource "aws_apigatewayv2_route" "api_route_OPTIONS" {
    api_id = aws_apigatewayv2_api.api.id
    route_key = "OPTIONS /${var.function_name}"
    target = "integrations/${aws_apigatewayv2_integration.api_integration.id}"
}

8. Outputs:

The output block exposes the generated API endpoint URL for future use.

output "api_URL" {
    value = aws_apigatewayv2_api.api.api_endpoint
}

Testing and Deployment

Before applying the Terraform configuration, ensure you have the Terraform CLI installed and configured with AWS credentials.

  1. Initialize Terraform: Run terraform init to download the required Terraform providers.
  2. Plan the changes: Run terraform plan to see a preview of Terraform’s infrastructure changes.
  3. Apply the changes: Once you’re satisfied with the plan, run terraform apply to create the infrastructure.

Demo

  • Image of Terraform init
  • Image of Terraform Plan
  • Image of Terraform Apply
  • Image of testing API
  • Image of Python Testing Code

Conclusion

This blog post demonstrates how Terraform can be used to automate the deployment of a serverless backend with AWS Lambda and API Gateway. This approach offers benefits like infrastructure as code, version control, and repeatable deployments.


Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.


Discover more from Pumoxi

Subscribe now to keep reading and get access to the full archive.

Continue reading