AWS Lambda is the work horse for any serverless computing implementation on the Amazon Cloud Platform and on my own blog it is the basis for many example solutions and implementations for end to end projects that I have written about here.
Why is the Function URL feature nice?
A very common architecture when using Lambda is to front it with an API, typically API Gateway or the excellent and in my opinion under utilized AppSync. It is well and good when Lambdas are directly invoked as part of an internal workflow in an application or system, but when you need to expose it to client side applications like websites or mobile apps or external server side applications as in the case of webhooks and such, you will need to front the Lambda as it is not possible to invoke it directly.
Even outside of these common scenarios with external access, sometimes when you need to do messaging between AWS regions where direct Lambda to Lambda invocation is not possible, you will need to integrate your Lambdas with a messaging solution like EventBridge…but sometimes it is just easier to front your Lambda in front of that API Gateway you might already have there without going through the overhead of setting up a queue or an event bus.
With all that context being said, AWS’ recent announcement to support Lambda Function URLs as an easy way to quickly front an http endpoint to invoke the Lambda got me pretty excited about some of the possibilities. After all, any of us working with Lambdas know that very often we write a Lambda to do just one thing – a true single microservice like processing a webhook call from a third party service or replicating some action in another AWS region for resiliency. With Function URLs, you can quickly configure and endpoint without going through the overheads I listed above.
What it (currently) lacks – More ways to authorize calls
This is a recent feature at the time of this writing and presently there are really only two authentication integration options out of the box.
IAM: Restricts the invocations to within your defined IAM roles based authenticated users only. For most practical use cases, this makes the Lambda an internal invocation only type function.
NONE: No checks, call is always allowed, Lambda is always invoked. You need to write authorization logic within your Lambda function.
For differentiation, with API Gateway or AppSync, you would have the ability to write Lambda Authorizers or even seamlessly use Cognito User Pool based authorization.
Why having more authorization integrations for this feature would make it nicer?
At least in my opinion…the good thing about having a Lambda Authorizer is nice serverless microservice based reuse. I can build a single authorizer to handle very similar calls where perhaps it can validate authorization tokens between server to server calls for both API gateway routes as well as individual Lambda Function URLs.
As it currently stands, we would perhaps need to build a Lambda Layer and package that with our microservices using function URLs to do the authorization checks. Certainly possible but breaks with the nice clean separation that we have gotten used to in having Lambda authorizers for API Gateway/AppSync.
Also we lose some of the other benefits of Lambda Authorizers like having TTLs on calls and such.
This can still be worked with for server to server integrations, where the overhead will probably be more involved is when using Cognito User Pools for authorizing user invocations to your Lambda functions.
Now there are probably very good reasons AWS may choose not to go this way, perhaps doing so makes every Lambda function http endpoint the equivalent of a full fledged API Gateway endpoint with all the overhead that it would entail and additional costs, but it would be nice to find a balance to support it.
There is always going to be a need for a full featured API Gateway for various reasons including grouping multiple related function under it but still having the option to authorize individual Lambdas in a similar fashion would be nice. As it is, I would certainly consider it for server to server calls like webhooks with a Lambda layer for authorization work, but probably not consider it for client application facing authenticated calls.
None of this of course matters if your Lambda microservice API is public and doesn’t need authorization. Then use this feature readily without thinking about it. Now lets move on to how to configure a url for a Lambda.
Configuring a URL for a Lambda from the Console
See the next section for the Cloudformation Definition.
From the console, there is a new side menu option in the Configuration tab of Lambda.
Which opens up the detail option availability.
Most of the settings are related to CORS settings which is all origins by default.
Regarding CORS…as I mentioned, for now I personally would probably not want to expose these Lambda for browser based access where authentication and role based access is important because that is probably when I would want to consider to continue using API Gateway and AppSync to front Lambdas for their authorization helper features, especially Cognito. In other words, CORS would be less relevant for me personally now.
But if it fits your use case and you are OK with building the authorization logic internally in your function, by all means! This is where you set CORS settings like allowed origins and such.
After you hit Save, a new function url is generated based on the settings you selected.
Notice the prominent warning that the URL is public – ensure you do have the authorization logic handled internally when using this.
Configuring a URL for a Lambda with a Cloudformation Template
Typically I have all my Lambdas maintained in Cloudformation stacks.
It is quite easy to define your url settings in Lambda yaml using the new FunctionUrlConfig property which has AuthType and Cors settings under it.
LambdaWithURL: Type: AWS::Serverless::Function Properties: Architectures: - arm64 Runtime: python3.9 CodeUri: ./lambda-with-url-demo/ Handler: lambda-with-url-demo.lambda_handler FunctionName: lambda-with-url-demo FunctionUrlConfig: AuthType: NONE Cors: AllowOrigins: - "*" AllowCredentials: true AllowMethods: - POST AllowHeaders: - authorization
That concludes my initial look at Lambda Function URLs. Hopefully I will have more to write about and update in the future.