AWS Lambda function URLs with .NET minimal api
In this article we are going to create a fully functional Github actions pipeline that deploys a .NET minimal api in AWS Lambda.
We will be using a Lambda feature released on (April 2022)— Lambda function Urls and CloudFront in order to configure a custom domain for that lambda. We will be embracing a full IaC approach with CloudFormation
TL;DR
You will find the complete repo with Github workflow here
👉https://github.com/ziedbentahar/aws-lambda-url-net6-minimal-api-sample
About Running Asp.net on AWS Lambda
Serverless functions should be simple and single responsibility oriented. Having a full fledged asp.net core application hosted as a Lambda might break that principle. Lambda functions should have a bounded responsibility so that it doesn’t do more work than it should. That way we avoid bloated Lambda-lith that may lead to long cold starts and performance issues.
But writing a single serverless function that performs CRUD operations (like createItem, updateItem, getItem) instead of creating a function for each operation can have some benefits: A better developer experience and a reduced workflow complexity.
Asp.net minimal api works well in that way : First It makes bootstrapping lambda code easy and with « minimal » boilerplate. Second, the low ceremony nature of minimal api matches the serverless functions philosophy.
Lambda function Urls
In this article we will be using lambda function URLs. This feature lets you configure an HTTPS endpoint in front of the function without configuring an Api Gateway or an Application Load balancer.
When enabled, Lambda generates a unique URL with this format
https://<a-uniquely-generated-url-id.lambda-url.<your-region>.on.aws
We will use a CloudFront distribution configured with a custom domain and we will set the lambda url as the distribution origin.
️️⚠️ A word of caution: Lambda function URLs does not provide features such as rate limiting, IP filtering or request authorization. Rate limiting and IP filtering can be managed with AWS WAF configuration used with CloudFront
Architecture overview
- S3 Serves as a lambda deployment bucket
- The Lambda is configured with functions URL feature
- CloudFront distribution is configured with our custom domain. We have to use a proper cache behavior that prevents from caching Lambda responses
- ACM Certificate for our custom domain, this certificate must be created in
us-east-1
since it will be used by CloudFront - An SSM Parameter where we will store the certificate Arn
- In this example we will deploy all the other components to
eu-west-1
region
As a prerequisite we will need to configure a public HostedZone for our domain name
Creating the .NET 6 minimal api
It’s pretty straight forward…and minimal
We will need to:
- Use
Amazon.Lambda.AspNetCoreServer.Hosting
Nuget package, in order to be able to callAddAWSLambdaHosting
extension method. - Specify
HttpApi
as the event source. - In this example I am enabling swagger and swagger ui. Depending on your use case, you will want to disable it for public access.
Creating the Lambda resource
Here are the relevant bits for the Lambda resource. In this example I am using AWS::Lambda::Function
instead of AWS::Serverless::Function
. We will use plain CloudFormation instead of AWS SAM.
On the Lambda Function resource, we will:
- Specify
dotnet6
as the Runtime - Define the assembly name as the value of
Handler
property
On the lambda Url resource we will need to set:
AuthType
toNONE
as we want it publicly accessibleTargetFunctionArn
to the Arn or the lambda function
And finally we will need to specify a lambda permission to allow Lambda invocation
Creating the Certificate
In order to use an ACM certificate with CloudFront. We must create it in us-east-1
region. To share the certificate Arn with another stack created in another region, we will use an SSM Parameter store. We will rely on a higher level construct (in our case the Github action job) that will “injects” the certificate Arn as a parameter of the stack defining the CloudFront resource
CloudFront distribution
The following template creates the CloudFront distribution
Here we define our custom domain as an alias for this distribution using the Aliases
Property. The ViewerCertificate.AcmCertificateArn
property will have the value of the certificate Arn defined earlier.
We will set the origin of this cf distribution to the function Url. The origin must be a domain name, so we will have to remove the non relevant parts of the Url : the leading https://
and the trailing /
And finally we disable lambda responses caching by using a managed cache policy CachePolicyId: '4135ea2d-6df8-44a3-9df3-4b5a84be39ad'
. You can learn more about managed caching policy here
Defining the RecordSetGroup
The requests to our custom domain must be routed to the CloudFront distribution: We will create two recordsets A
IPV4 and AAAA
IPV6 aliases records pointing our custom domain to the CloudFront distribution
Alright, on the previous section we defined the CloudFormation resources. Let’s focus on the CI/CD pipeline now
Reminder
You can find the complete repo with Github workflow here: 👉https://github.com/ziedbentahar/aws-lambda-url-net6-minimal-api-sample
Github Actions Workflow
Before we start we will first add AWS Access Key and AWS Secret key to the project secrets. (As a best practice, you should use temporary security credentials instead of access keys, but this will not be covered in the article)
Defining the pipeline
This pipeline will be have three main jobs: build, publish to s3 and deploy
1 — The build step is pretty straight forward: dotnet build
→ dotnet test
→ dotnet publish
→ finally zip and upload the artifact
We are using the commit hash
2 — Publish to s3 will take the build step artifact and upload it to the lambda bucket
But first we will ensure that lambda bucket is created (or updated) and we will use the git commit SHA as part of the uploaded file name.
3 — And the final step, is where the deployment to AWS Lambda service happens
As mentioned earlier, we will need to create the ACM certificate to us-east-1
region and then write it’s Arn to a SSM parameter store. This is what Deploy certificate
step does by invoking aws cloudformation deploy — template-file ./aws/cfn/certificate.yml …
Next, in the Get Certificate Arn
Get step we will retrieve the certificate Arn from the parameter store and set it as en env variable. We can use it in the next step by injecting it as parameter of the Lambda stack.
And finally, Deploy Lambda
step will update (or create) the lambda stack 🎉
Update — 29/10/2022
If you want to use an API Gateway instead of exposing directly your Lambda through function URLs, you can find in this repo a complete solution with its Github actions CI/CD pipeline 👇
https://github.com/ziedbentahar/aws-lambda-api-gateway-net6-minimal-api-sample