Edge lambda functions
Overview
Edge lambda function is a scalable and highly available computing resource that runs your code closer to users of your application, improving performance and latency.
Functions are globally distributed across regional CDN cache locations (13 locations spread across the globe), which makes it possible to execute them closer to the end user
Lambda functions are "serverless" and fully managed. You don't have to worry about provisioning and managing servers, container and OS security, patching, scaling & many other DevOps tasks.
Supported runtimes are Node.js (Javascript and Typescript) and Python.
Use-cases for Edge lambda functions: performing user authentication, cookie inspection, validation of requests, and many more.
Advantages
- Low latency - By running code closer to end user, latency and overall performance is improved.
- Scalability - Run your code without neccessity to manage any infrastructure. Similarly to regular functions, Edge lambda functions provide high scalability.
- Pay-per-use - You only pay for the compute time you consume (rounded to 1ms). Refer to Lambda@Edge pricing in AWS docs.
Disadvantages
- Limited execution environment - (Applies to
onRequest
/onResponse
lambdas) Your Edge lambda functions are limited to 128MB of memory and maximum execution time of 5 seconds.
Basic usage
You can associate
edge-lambda-function
with CDN to be executed:onRequest
- function is executed when CDN receives a request from a client(viewer) before checking CDN cacheonResponse
- function is executed before returning the response to the client(viewer)
Potential use-cases for using edge functions:
- generating immediate HTTP response without the need to check CDN cache or forward to origin
- modifying request (i.e rewrite url, headers etc) before forwarding to the origin
- inspection of cookies
- inspection/validation of authorization headers and tokens
In the following example we are using edge-lambda-function
to validate Authorization
header of each incoming user
request. If user is not authorized, he is redirected to login page.
Copy
resources:authFunction:type: edge-lambda-functionproperties:packaging:type: stacktape-lambda-buildpackproperties:entryfilePath: auth-function.tsmyBucket:type: bucketproperties:cdn:enabled: trueedgeFunctions:onRequest: authFunction
Copy
const validateAuthorizationToken = (token) => {// perform some validationreturn true;};export default async (event) => {const { request } = event.Records[0].cf;const { headers } = request;const authorizationToken = headers.authorization?.[0]?.value;const userAuthorized = validateAuthorizationToken(authorizationToken);// if user is not authorized, redirect him to login pageif (!userAuthorized) {return {status: '302',headers: {location: [{key: 'Location',value: '/login'}],'cache-control': [{key: 'Cache-Control',value: 'no-cache, no-store, max-age=0, must-revalidate'}]}};}// after we validated that user is authorized, we can return the request// request will be forwarded to originreturn request;};
Code of authorizer function authFunction
Event structure
Event delivered to the function differs based on whether you are using onRequest
or onResponse
trigger:
On request trigger
When using onRequest
trigger with edge-lambda-function you can:
- Return response from function - In this case the response is served to the client directly and the request is NOT forwarded to the origin.
- Forward request to origin - You can modify the request (path, body, headers ...) before forwarding to origin or you can forward it as is.
Following example code combines both options:
- If user is authorized - forwards request to the origin
- If user is not authorized - return redirect response to login page
Copy
const validateAuthorizationToken = (token) => {// perform some validationreturn true;};export default async (event) => {const { request } = event.Records[0].cf;const { headers } = request;const authorizationToken = headers.authorization?.[0]?.value;const userAuthorized = validateAuthorizationToken(authorizationToken);// if user is not authorized, redirect him to login pageif (!userAuthorized) {return {status: '302',headers: {location: [{key: 'Location',value: '/login'}],'cache-control': [{key: 'Cache-Control',value: 'no-cache, no-store, max-age=0, must-revalidate'}]}};}// after we validated that user is authorized, we can return the request// request will be forwarded to originreturn request;};
Code of authorizer function
Copy
resources:authFunction:type: edge-lambda-functionproperties:packaging:type: stacktape-lambda-buildpackproperties:entryfilePath: auth-function.tsmyBucket:type: bucketproperties:cdn:enabled: trueedgeFunctions:onRequest: authFunction
Example config
On response trigger
When using onResponse
trigger with edge-lambda-function you can modify the response before returning.
In following example we are using it to set response cookie.
Copy
export default async (event) => {const { response } = event.Records[0].cf;response.headers['set-cookie'] = [{key: 'Set-Cookie',value: 'my-experimental-cookie=cookie-value'}];return response;};
Code of the function
Copy
resources:cookieFunction:type: edge-lambda-functionproperties:packaging:type: stacktape-lambda-buildpackproperties:entryfilePath: set-cookie.tsmyBucket:type: bucketproperties:cdn:enabled: trueedgeFunctions:onResponse: cookieFunction
Example config
Packaging
Refer to packaging docs.
When using cdn-lambda function with onReqeust
or onResponse
trigger the zipped package size is limited to 1 MB.
Logging
- Information about the function invocation and function logs (stdout and stderr) are automatically sent to a pre-created CloudWatch log group.
- By default, logs are retained for 180 days..
- You can see the logs in the log group page in the AWS CloudWatch console. You can use
stacktape stack-info
command to get a direct link.
Edge lambda functions are executed in multiple locations (regions) across the world depending on the cdn edge location which serves the client request (usually the location closest to the client).
As a result the logs for the function executed in given region are delivered to a log group in that region. The list of regions where edge lambda can execute: us-east-1, us-east-2, us-west-1, us-west-2, ap-south-1, ap-northeast-1, ap-northeast-2, ap-southeast-1, ap-southeast-2, eu-west-1, eu-west-2, eu-central-1, sa-east-1
Accessing other resources
For most of the AWS resources, resource-to-resource communication is not allowed by default. This helps to enforce security and resource isolation. Access must be explicitly granted using IAM (Identity and Access Management) permissions.
Stacktape automatically handles IAM permissions for the underlying AWS services that it creates (i.e. granting cdn lambda function permission to write logs to Cloudwatch).
If your function needs to communicate with other infrastructure components, you need to add permissions manually. You can do this in 2 ways listed below.
Using allowAccessTo
- List of resource names or AWS services that this function will be able to access (basic IAM permissions will be granted automatically). Granted permissions differ based on the resource.
- Works only for resources managed by Stacktape (not arbitrary Cloudformation resources)
- This is useful if you don't want to deal with IAM permissions yourself. Handling permissions using raw IAM role statements can be cumbersome, time-consuming and error-prone.
Copy
resources:authFunction:type: edge-lambda-functionproperties:packaging:type: stacktape-lambda-buildpackproperties:entryfilePath: auth-function.tsaccessControl:allowAccessTo:# access to the dynamo table- myDynamoTable# access to AWS SES- aws:sesmyDynamoTable:type: dynamo-db-tableproperties:primaryKey:partitionKey:name: idtype: string
Using iamRoleStatements
- IAM Role statements are a low-level, granular and AWS-native way of controlling access to your resources.
- IAM Role statements can be used to add permissions to any Cloudformation resource.
- Configured IAM role statement objects will be appended to the function's role.
Be advised when accessing resources from edge-lambda-functions. While your stack (and the resources you are accessing) are deployed in a specific region, the function can be executed in any edge location around the world. This might result in higher latencies when function is accessing the resource from a more distant region.
Example:
- You have stack with a bucket and a edge lambda function (which has access gratned to the bucket) deployed in region eu-west-1.
- Request to CDN comes to us-east-1 region.
- The function is executed in us-east-1 region. Due to network delays the communication between the function and the bucket might be slower.