AWS Lambda Function URL using Terraform -  quick walkthrough

AWS Lambda Function URL using Terraform - quick walkthrough

How many times have you had to create an API Gateway resource on AWS just to get through to your Lambda Function? I love using the serverless stack (AWS Lambda + API Gateway + DynamoDB) for running small applications. The Lambda and APIGateway combo has served its purpose really well. Last week, AWS announced Lambda Function URLs. A new Lambda feature that allows you to directly create an HTTPS endpoint for your Function.

Let's walk through how we can quickly set this up with terraform.

First, create the provider and terraform blocks in provider.tf.

touch provider.tf
terraform {
  required_providers {
    aws = {
      version = ">= 4.9.0”
      source = "hashicorp/aws"
    }
  }
}
provider "aws" {
  profile = “your_cli_profile”
  region  = "us-east-1"
}

The most recent release of the terraform-provider-aws (v4.9.0) by Hashicorp has the lambda functions URLs functionality. Make sure to replace profile with your cli profile. If you don’t already have an AWS access profile setup refer to the AWS documentation to set it up.

Creating the Lambda URL resource

Now we will go ahead and create the lambda functions URL resource.

touch main.tf
resource "aws_lambda_function_url" "url1" {
  function_name      = aws_lambda_function.myfunc.function_name
  authorization_type = "NONE"

  cors {
    allow_credentials = true
    allow_origins     = ["*"]
    allow_methods     = ["*"]
    allow_headers     = ["date", "keep-alive"]
    expose_headers    = ["keep-alive", "date"]
    max_age           = 86400
  }
}

Note that the authorization_type = 'NONE' makes your URL publicly accessible. This might be fine for testing. In higher environments, however, this must be avoided. We set our familiar CORS values including allow_methods which can be set to "GET", "POST", "DELETE".

Before we can run this, we need a full lambda function resource, myfunc.

resource "aws_lambda_function" "myfunc" {
  filename         = data.archive_file.zip.output_path
  source_code_hash = data.archive_file.zip.output_base64sha256
  function_name    = "myfunc"
  role             = aws_iam_role.iam_for_lambda.arn
  handler          = "func.handler"
  runtime          = "python3.8"

}

resource "aws_iam_role" "iam_for_lambda" {
  name = "iam_for_lambda"

  assume_role_policy = <<EOF
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Action": "sts:AssumeRole",
      "Principal": {
        "Service": "lambda.amazonaws.com"
      },
      "Effect": "Allow",
      "Sid": ""
    }
  ]
}
EOF
}

data "archive_file" "zip" {
  type        = "zip"
  source_dir = "${path.module}/lambda/"
  output_path = "${path.module}/packedlambda.zip"
}

We will use Python for the handler function.

mkdir lambda && touch lambda/func.py
def handler(event, context):
    body = "hello"
    response = {
        "statusCode": 200,
        "statusDescription": "200 OK",
        "isBase64Encoded": False,
        "headers": {"Content-Type": "text/json; charset=utf-8"},
        "body": body
        }

    return response

Let's run terraform

To run terraform we go through the workflow steps:

terraform init
terraform plan
terraform apply

Now let's go into the console to look at, and try out our new Lambda Function URL.

Screen Shot 2022-04-11 at 8.07.34 PM.jpg

Final thoughts

Congrats! you just created a Lambda Function URL, a simple API to your serverless lambda function. Lambda Function URLs do not have native APIGateway capabilities such as rate limiting, throttling, IP whitelisting / blacklisting, authorizers. There has been a lot of talk about this in the wider developer community and concerns about how security best practices can be implemented.
On the plus side they:

  • have no added cost on top of Lambda

  • are quicker to implement than API Gateway

  • can be paired with CloudFront to tap some of its inherent benefits such as WAF, Logging, geo targeting - targeting delivery to specific end-users.

Here's the github code for the project.