Amazon Web Services (AWS) recently announced Function URLs, a new in-built feature that allows you to invoke your functions through an HTTPS endpoint. By default, the endpoint is secure using AWS Identity Access Management (IAM) but you can allow public access with optional Cross-Origin Resource Sharing (CORS) configurations and/or custom authorisation logic. Originally, if you wanted to invoke a Lambda function publicly via HTTPS you would need to set up and configure AWS API Gateway or AWS Elastic Load Balancing and pay additional fees once you exceeded their free tier. Fortunately, Function URLs don't incur an additional cost π. Iβd recommend you continue to use these services if youβre building a serverless REST API or require additional features such as request-response transformations. For small use-cases such as webhooks or determining the price of a cryptocurrency, Function URLs are more suited.
This blog post will demonstrate how to create an HTTPS Lambda endpoint using Function URLs, Python and Terraform, an open-source infrastructure as code tool. If youβd rather not use Terraform, Function URLs can be created directly via the AWS user interface (UI). You can follow the official AWS guide here. You can use any compatible programming language with AWS Lambda for this demonstration since the principles are the same. You can view the project's source code on Github.
First, create a Python project with main.py being the entry point for Lambda. I recommend using this modern Python development environment for a more straightforward implementation but your own will suffice. This project will use the Python version 3.9.0 . You can view a list of supported versions here. Your project directory structure should replicate this:
.
βββ .editorconfig
βββ CHANGELOG.md
βββ README.md
βββ lambda_function_url_terraform
β βββ __init__.py
β βββ main.py
βββ poetry.lock
βββ pyproject.toml
βββ setup.cfg
βββ tests
βββ __init__.py
βββ test_main.py
2 directories, 10 files
For this example, the main.py Lambda handler will return a JSON object with a body containing a message and status code of 200.
To ensure the Lambda handler is working as expected write a unit test in tests/test_main.py to validate its response.
If you donβt have Terraform already installed, you can follow the [official installation documentation.](http://HashiCorp distributes Terraform as a binary package. You can also install Terraform using popular package managers.) Once installed, confirm the installation was successful by executing:
First, create the required Terraform deployment file main.tf at the top level of your Python project. Declare 1.0.0 as the Terraform version and 4.9.0 as the Hashicorp AWS provider version since that's when Function URLs functionality was implemented. You can review the merge request here. Next, declare the AWS region, for example eu-west-1 . Once declared main.tf should look like this:
Before the Lambda function can be implemented, an IAM role with a trust policy needs to be created. In this case, the AWS Lambda service will be trusted and allowed to call the AWS Security Token Service (STS) AssumeRole action. Append the IAM role resource to main.tf file. Its implementation should look like this:
Execute the following commands to create a zip file called package.zip containing the projects source code and its requirements for Lambda:
# Install zip package to zip files/folders:
sudo apt-get install zip
poetry build;
poetry run pip install --upgrade -t package dist/*.whl;
(cd package; zip -r ../package.zip . -x '*.pyc';)
# Pip installation without Poetry or zip:
pip freeze > requirements.txt
pip install -r requirements.txt -t package
# zip the package folder.
Once packaged the Lambda function is ready to be implemented. Depending on your setup you may need to modify the following attributes:
-
runtime depending on your Python version.
-
function_name the name you want to give your Lambda function.
-
handler the path to your Lambda handler.
The Lambda function resource should look like this:
The filename attribute is the filename along with the extension of our packaged project. Similarly, the source_code_hash attribute is used to determine if the packaged project has been updated, i.e. a code change. The role attribute is a reference to the previously implemented IAM role. Append the Lambda function to main.tf .
Lastly, create the Function URL resource and save the generated URL.The authorization_type is set to NONE , meaning it allows public access. You have the option of restricting access to authenticated IAM users only, as well as CORS configuration capabilities. You can read about them here. The Lambda Function URL resource should look like this:
The output resource function_url saves the generated Function URL. Append both the Function URL and output resource to main.tf . With all the Terraform components together, main.tf should replicate this: {% gist https://gist.github.com/ShaneNolan/4d0c32d3db2976b180470769928ce5e2.js %} Deploying with Terraform requires only a single command after the infrastructure is coded but first, you need to initialise Terraform inside of the project by executing terraform init . Additionally, set your AWS_ACCESS_KEY_ID , AWS_SECRET_ACCESS_KEY and AWS_REGION via the command line. If youβre unfamiliar with configuring your AWS credentials you can read more about it on the official AWS and Terraform documentation. Once initialised, deploy your Lambda function using terraform apply and accept the confirmation of the required changes. After deployment, it will output the Lambda Function URL π.
Test the public endpoint by either opening the URL in a browser or using an API testing tool such as httpie. The below example uses Terraform to retrieve the generated Function URL via terraform output and a GET request is submitted to the URL via httpie.