Files
swift-aws-lambda-runtime/Examples/HelloWorldNoTraits
Sébastien Stormacq 2abe7eb7de Add support for Lambda Tenants (#608)
Address https://github.com/awslabs/swift-aws-lambda-runtime/issues/605

NEW Lambda Tenant isolation capability: 
https://docs.aws.amazon.com/lambda/latest/dg/tenant-isolation.html 


# Add Support for Lambda Tenant Isolation Mode

## Summary

This PR adds support for AWS Lambda's tenant isolation mode to the Swift
AWS Lambda Runtime, enabling developers to build multi-tenant
applications with strict execution environment isolation per tenant.

## Changes

### Runtime Support
- Added `tenantID` property to `LambdaContext` to expose the tenant
identifier
- Extended `InvocationMetadata` to capture the
`Lambda-Runtime-Aws-Tenant-Id` header
- Added `AmazonHeaders.tenantID` constant for the tenant ID header
- Added trace logging for invocation headers to aid debugging

### New Example: MultiTenant
A complete working example demonstrating tenant isolation mode:
- **Request tracking system** that maintains separate counters and
histories per tenant
- **Actor-based storage** (`TenantDataStore`) for thread-safe tenant
data management
- **Immutable data structures** (`TenantData`) following Swift best
practices
- **API Gateway integration** with tenant ID passed via query parameter
- **SAM template** configured with `TenancyConfig.TenantIsolationMode:
PER_TENANT`
- **Comprehensive documentation** covering architecture, deployment,
testing, and best practices

### Testing
- Added unit test for tenant ID extraction from invocation headers
- Integrated MultiTenant example into CI/CD pipeline

### Documentation
The example includes detailed documentation on:
- When to use tenant isolation (user code execution, sensitive data
processing)
- How tenant isolation works (dedicated environments, no cross-tenant
reuse)
- Concurrency limits and scaling considerations
- Pricing implications
- Security best practices
- CloudWatch monitoring with tenant dimensions

## Files Changed
- `Sources/AWSLambdaRuntime/LambdaContext.swift` - Added tenantID
property
- `Sources/AWSLambdaRuntime/ControlPlaneRequest.swift` - Capture tenant
ID from headers
- `Sources/AWSLambdaRuntime/Utils.swift` - Added tenantID header
constant
- `Sources/AWSLambdaRuntime/Lambda.swift` - Pass tenant ID to context
- `Sources/AWSLambdaRuntime/LambdaRuntimeClient+ChannelHandler.swift` -
Added trace logging
- `Tests/AWSLambdaRuntimeTests/InvocationTests.swift` - Added tenant ID
test
- `Examples/MultiTenant/*` - New complete example with SAM template
- `.github/workflows/pull_request.yml` - Added MultiTenant to CI
pipeline

## Testing Instructions

1. Build and deploy the example:
   bash
  cd Examples/MultiTenant
  swift package archive --allow-network-connections docker
  sam deploy --guided
  

2. Test with different tenants:
   bash
curl
"https://<api-id>.execute-api.<region>.amazonaws.com/Prod?tenant-id=
alice"
curl
"https://<api-id>.execute-api.<region>.amazonaws.com/Prod?tenant-id=
bob"
  


3. Verify isolation by checking that each tenant maintains separate
request counts

## Related Documentation
- [AWS Lambda Tenant
Isolation](https://docs.aws.amazon.com/lambda/latest/dg/tenant-isolation.html)
- [AWS Blog: Streamlined Multi-Tenant Application
Development](https://aws.amazon.com/blogs/aws/streamlined-multi-tenant-application-development-with-tenant-isolation-mode-in-aws-lambda/)

---------

Co-authored-by: Sebastien Stormacq <stormacq@amazon.lu>
Co-authored-by: Tim Condon <0xTim@users.noreply.github.com>
2025-11-21 21:14:15 +01:00
..

Hello World, with no traits

This is an example of a low-level AWS Lambda function that takes a ByteBuffer as input parameter and writes its response on the provided LambdaResponseStreamWriter.

This function disables all the default traits: the support for JSON Encoder and Decoder from Foundation, the support for Swift Service Lifecycle, and for the local tetsing server.

The main reasons of the existence of this example are

  1. to show how to write a low-level Lambda function that doesn't rely on JSON encodinga and decoding.
  2. to show you how to disable traits when using the Lambda Runtime Library.
  3. to add an integration test to our continous integration pipeline to make sure the library compiles with no traits enabled.

Disabling all traits

Traits are functions of the AWS Lambda Runtime that you can disable at compile time to reduce the size of your binary, and therefore reduce the cold start time of your Lambda function.

The library supports three traits:

  • "FoundationJSONSupport": adds the required API to encode and decode payloads with Foundation's JSONEncoder and JSONDecoder.

  • "ServiceLifecycleSupport": adds support for the Swift Service Lifecycle library.

  • "LocalServerSupport": adds support for testing your function locally with a built-in HTTP server.

This example disables all the traits. To disable one or several traits, modify Package.swift:

	dependencies: [
			.package(url: "https://github.com/awslabs/swift-aws-lambda-runtime.git", from: "2.0.0-beta", traits: [])
	],

Code

The code creates a LambdaRuntime struct. In its simplest form, the initializer takes a function as argument. The function is the handler that will be invoked when an event triggers the Lambda function.

The handler signature is (event: ByteBuffer, response: LambdaResponseStreamWriter, context: LambdaContext).

The function takes three arguments:

  • the event argument is a ByteBuffer. It's the parameter passed when invoking the function. You are responsible of decoding this parameter, if necessary.
  • the response writer provides you with functions to write the response stream back.
  • the context argument is a Lambda Context. It is a description of the runtime context.

The function return value will be encoded as your Lambda function response.

Test locally

You cannot test this function locally, because the "LocalServer" trait is disabled.

Build & Package

To build & archive the package, type the following commands.

swift build
swift package archive --allow-network-connections docker

If there is no error, there is a ZIP file ready to deploy. The ZIP file is located at .build/plugins/AWSLambdaPackager/outputs/AWSLambdaPackager/MyLambda/MyLambda.zip

Deploy

Here is how to deploy using the aws command line.

aws lambda create-function \
--function-name MyLambda \
--zip-file fileb://.build/plugins/AWSLambdaPackager/outputs/AWSLambdaPackager/MyLambda/MyLambda.zip \
--runtime provided.al2 \
--handler provided  \
--architectures arm64 \
--role arn:aws:iam::<YOUR_ACCOUNT_ID>:role/lambda_basic_execution

The --architectures flag is only required when you build the binary on an Apple Silicon machine (Apple M1 or more recent). It defaults to x64.

Be sure to replace <YOUR_ACCOUNT_ID> with your actual AWS account ID (for example: 012345678901).

Invoke your Lambda function

To invoke the Lambda function, use this aws command line.

aws lambda invoke \
--function-name MyLambda \
--payload $(echo "Seb" | base64)  \
out.txt && cat out.txt && rm out.txt

This should output the following result.

{
    "StatusCode": 200,
    "ExecutedVersion": "$LATEST"
}
"Hello World!"

Undeploy

When done testing, you can delete the Lambda function with this command.

aws lambda delete-function --function-name MyLambda

⚠️ Security and Reliability Notice

These are example applications for demonstration purposes. When deploying such infrastructure in production environments, we strongly encourage you to follow these best practices for improved security and resiliency: