mirror of
https://github.com/swift-server/swift-aws-lambda-runtime.git
synced 2026-05-03 07:22:27 +00:00
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>
This commit is contained in:
committed by
GitHub
parent
0305cb31b8
commit
2abe7eb7de
@@ -2,3 +2,4 @@ response.json
|
||||
samconfig.toml
|
||||
template.yaml
|
||||
Makefile
|
||||
Dockerfile
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
response.json
|
||||
samconfig.toml
|
||||
Makefile
|
||||
@@ -0,0 +1,55 @@
|
||||
// swift-tools-version:6.2
|
||||
|
||||
import PackageDescription
|
||||
|
||||
// needed for CI to test the local version of the library
|
||||
import struct Foundation.URL
|
||||
|
||||
let package = Package(
|
||||
name: "swift-aws-lambda-runtime-example",
|
||||
platforms: [.macOS(.v15)],
|
||||
products: [
|
||||
.executable(name: "MultiTenant", targets: ["MultiTenant"])
|
||||
],
|
||||
dependencies: [
|
||||
// during CI, the dependency on local version of swift-aws-lambda-runtime is added dynamically below
|
||||
.package(url: "https://github.com/awslabs/swift-aws-lambda-runtime.git", from: "2.0.0"),
|
||||
.package(url: "https://github.com/awslabs/swift-aws-lambda-events.git", from: "1.0.0"),
|
||||
],
|
||||
targets: [
|
||||
.executableTarget(
|
||||
name: "MultiTenant",
|
||||
dependencies: [
|
||||
.product(name: "AWSLambdaRuntime", package: "swift-aws-lambda-runtime"),
|
||||
.product(name: "AWSLambdaEvents", package: "swift-aws-lambda-events"),
|
||||
]
|
||||
)
|
||||
]
|
||||
)
|
||||
|
||||
if let localDepsPath = Context.environment["LAMBDA_USE_LOCAL_DEPS"],
|
||||
localDepsPath != "",
|
||||
let v = try? URL(fileURLWithPath: localDepsPath).resourceValues(forKeys: [.isDirectoryKey]),
|
||||
v.isDirectory == true
|
||||
{
|
||||
// when we use the local runtime as deps, let's remove the dependency added above
|
||||
let indexToRemove = package.dependencies.firstIndex { dependency in
|
||||
if case .sourceControl(
|
||||
name: _,
|
||||
location: "https://github.com/awslabs/swift-aws-lambda-runtime.git",
|
||||
requirement: _
|
||||
) = dependency.kind {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
if let indexToRemove {
|
||||
package.dependencies.remove(at: indexToRemove)
|
||||
}
|
||||
|
||||
// then we add the dependency on LAMBDA_USE_LOCAL_DEPS' path (typically ../..)
|
||||
print("[INFO] Compiling against swift-aws-lambda-runtime located at \(localDepsPath)")
|
||||
package.dependencies += [
|
||||
.package(name: "swift-aws-lambda-runtime", path: localDepsPath)
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,316 @@
|
||||
# Multi-Tenant Lambda Function Example
|
||||
|
||||
This example demonstrates how to build a multi-tenant Lambda function using Swift and AWS Lambda's tenant isolation mode. Tenant isolation ensures that execution environments are dedicated to specific tenants, providing strict isolation for processing tenant-specific code or data.
|
||||
|
||||
## Overview
|
||||
|
||||
This example implements a request tracking system that maintains separate counters and request histories for each tenant. The Lambda function:
|
||||
|
||||
- Accepts requests from multiple tenants via API Gateway
|
||||
- Maintains isolated execution environments per tenant
|
||||
- Tracks request counts and timestamps for each tenant
|
||||
- Returns tenant-specific data in JSON format
|
||||
|
||||
## What is Tenant Isolation Mode?
|
||||
|
||||
AWS Lambda's tenant isolation mode routes requests to execution environments based on a customer-specified tenant identifier. This ensures that:
|
||||
|
||||
- **Execution environments are never reused across different tenants** - Each tenant gets dedicated execution environments
|
||||
- **Data isolation** - Tenant-specific data remains isolated from other tenants
|
||||
- **Firecracker virtualization** - Provides workload isolation at the infrastructure level
|
||||
|
||||
### When to Use Tenant Isolation
|
||||
|
||||
Use tenant isolation mode when building multi-tenant applications that:
|
||||
|
||||
- **Execute end-user supplied code** - Limits the impact of potentially incorrect or malicious user code
|
||||
- **Process tenant-specific data** - Prevents exposure of sensitive data to other tenants
|
||||
- **Require strict isolation guarantees** - Such as SaaS platforms for workflow automation or code execution
|
||||
|
||||
## Architecture
|
||||
|
||||
The example consists of:
|
||||
|
||||
1. **TenantData** - Immutable struct tracking tenant information:
|
||||
- `tenantID`: Unique identifier for the tenant
|
||||
- `requestCount`: Total number of requests from this tenant
|
||||
- `firstRequest`: Unix timestamp (seconds since epoch) of the first request
|
||||
- `requests`: Array of individual request records
|
||||
|
||||
2. **TenantDataStore** - Actor-based storage providing thread-safe access to tenant data across invocations
|
||||
|
||||
3. **Lambda Handler** - Processes API Gateway requests and manages tenant data
|
||||
|
||||
## Code Structure
|
||||
|
||||
```swift
|
||||
// Immutable tenant data structure
|
||||
struct TenantData: Codable {
|
||||
let tenantID: String
|
||||
let requestCount: Int
|
||||
let firstRequest: String
|
||||
let requests: [TenantRequest]
|
||||
|
||||
func addingRequest() -> TenantData {
|
||||
// Returns new instance with incremented count
|
||||
}
|
||||
}
|
||||
|
||||
// Thread-safe tenant storage using Swift actors
|
||||
actor TenantDataStore {
|
||||
private var tenants: [String: TenantData] = [:]
|
||||
|
||||
subscript(id: String) -> TenantData? {
|
||||
tenants[id]
|
||||
}
|
||||
|
||||
func update(id: String, data: TenantData) {
|
||||
tenants[id] = data
|
||||
}
|
||||
}
|
||||
|
||||
// Lambda handler extracts tenant ID from context
|
||||
let runtime = LambdaRuntime {
|
||||
(event: APIGatewayRequest, context: LambdaContext) -> APIGatewayResponse in
|
||||
|
||||
guard let tenantID = context.tenantID else {
|
||||
return APIGatewayResponse(statusCode: .badRequest, body: "No Tenant ID provided")
|
||||
}
|
||||
|
||||
// Process request for this tenant
|
||||
let currentData = await tenants[tenantID] ?? TenantData(tenantID: tenantID)
|
||||
let updatedData = currentData.addingRequest()
|
||||
await tenants.update(id: tenantID, data: updatedData)
|
||||
|
||||
return try APIGatewayResponse(statusCode: .ok, encodableBody: updatedData)
|
||||
}
|
||||
```
|
||||
|
||||
## Configuration
|
||||
|
||||
### SAM Template (template.yaml)
|
||||
|
||||
The function is configured with tenant isolation mode and API Gateway parameter mapping in the SAM template:
|
||||
|
||||
```yaml
|
||||
# API Gateway REST API with parameter mapping
|
||||
MultiTenantApi:
|
||||
Type: AWS::Serverless::Api
|
||||
Properties:
|
||||
StageName: Prod
|
||||
DefinitionBody:
|
||||
openapi: 3.0.1
|
||||
paths:
|
||||
/:
|
||||
get:
|
||||
parameters:
|
||||
- name: tenant-id
|
||||
in: query
|
||||
required: true
|
||||
x-amazon-apigateway-integration:
|
||||
type: aws_proxy
|
||||
httpMethod: POST
|
||||
uri: !Sub arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${MultiTenantLambda.Arn}/invocations
|
||||
# Map query parameter to Lambda tenant header
|
||||
requestParameters:
|
||||
integration.request.header.X-Amz-Tenant-Id: method.request.querystring.tenant-id
|
||||
|
||||
# Lambda function with tenant isolation
|
||||
MultiTenantLambda:
|
||||
Type: AWS::Serverless::Function
|
||||
Properties:
|
||||
Runtime: provided.al2023
|
||||
Architectures:
|
||||
- arm64
|
||||
# Enable tenant isolation mode
|
||||
TenancyConfig:
|
||||
TenantIsolationMode: PER_TENANT
|
||||
```
|
||||
|
||||
### Key Configuration Points
|
||||
|
||||
- **TenancyConfig.TenantIsolationMode**: Set to `PER_TENANT` to enable tenant isolation
|
||||
- **Parameter Mapping**: API Gateway maps the `tenant-id` query parameter to the `X-Amz-Tenant-Id` header required by Lambda
|
||||
- **REST API**: Uses REST API (not HTTP API) to support request parameter mapping
|
||||
- **OpenAPI Definition**: Defines the integration using OpenAPI 3.0 specification for fine-grained control
|
||||
- **Immutable property**: Tenant isolation can only be enabled when creating a new function
|
||||
- **Required tenant-id**: All invocations must include a tenant identifier
|
||||
|
||||
### Why Parameter Mapping is Required
|
||||
|
||||
Lambda's tenant isolation feature requires the tenant ID to be passed via the `X-Amz-Tenant-Id` header. When using API Gateway:
|
||||
|
||||
1. **Client sends request** with `tenant-id` as a query parameter
|
||||
2. **API Gateway transforms** the query parameter into the `X-Amz-Tenant-Id` header
|
||||
3. **Lambda receives** the header and routes to the appropriate tenant-isolated environment
|
||||
|
||||
This mapping is configured in the `x-amazon-apigateway-integration` section using:
|
||||
```yaml
|
||||
requestParameters:
|
||||
integration.request.header.X-Amz-Tenant-Id: method.request.querystring.tenant-id
|
||||
```
|
||||
|
||||
## Deployment
|
||||
|
||||
### Prerequisites
|
||||
|
||||
- Swift (>=6.2)
|
||||
- Docker (for cross-compilation to Amazon Linux)
|
||||
- AWS SAM CLI (>=1.147.1)
|
||||
- AWS CLI configured with appropriate credentials
|
||||
|
||||
### Build and Deploy
|
||||
|
||||
1. **Build the Lambda function**:
|
||||
```bash
|
||||
swift package archive --allow-network-connections docker
|
||||
```
|
||||
|
||||
2. **Deploy using SAM**:
|
||||
```bash
|
||||
sam deploy --guided
|
||||
```
|
||||
|
||||
3. **Note the API Gateway endpoint** from the CloudFormation outputs
|
||||
|
||||
## Testing
|
||||
|
||||
### Using API Gateway
|
||||
|
||||
The tenant ID is passed as a query parameter. API Gateway automatically maps it to the `X-Amz-Tenant-Id` header:
|
||||
|
||||
```bash
|
||||
# Request from tenant "alice"
|
||||
curl "https://your-api-id.execute-api.us-east-1.amazonaws.com/Prod?tenant-id=alice"
|
||||
|
||||
# Request from tenant "bob"
|
||||
curl "https://your-api-id.execute-api.us-east-1.amazonaws.com/Prod?tenant-id=bob"
|
||||
|
||||
# Multiple requests from the same tenant will reuse the execution environment
|
||||
for i in {1..5}; do
|
||||
curl "https://your-api-id.execute-api.us-east-1.amazonaws.com/Prod?tenant-id=alice"
|
||||
done
|
||||
```
|
||||
|
||||
### Using AWS CLI (Direct Lambda Invocation)
|
||||
|
||||
For direct Lambda invocation without API Gateway:
|
||||
|
||||
```bash
|
||||
# Synchronous invocation
|
||||
aws lambda invoke \
|
||||
--function-name MultiTenantLambda \
|
||||
--tenant-id alice \
|
||||
response.json
|
||||
|
||||
# View the response
|
||||
cat response.json
|
||||
```
|
||||
|
||||
### Expected Response
|
||||
|
||||
```json
|
||||
{
|
||||
"tenantID": "alice",
|
||||
"requestCount": 3,
|
||||
"firstRequest": "1705320000.123456",
|
||||
"requests": [
|
||||
{
|
||||
"requestNumber": 1,
|
||||
"timestamp": "1705320000.123456"
|
||||
},
|
||||
{
|
||||
"requestNumber": 2,
|
||||
"timestamp": "1705320075.789012"
|
||||
},
|
||||
{
|
||||
"requestNumber": 3,
|
||||
"timestamp": "1705320150.345678"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
**Note**: Timestamps are Unix epoch times (seconds since January 1, 1970) for cross-platform compatibility.
|
||||
|
||||
## How Tenant Isolation Works
|
||||
|
||||
1. **Request arrives** with a tenant identifier (via query parameter, header, or direct invocation)
|
||||
2. **Lambda routes the request** to an execution environment dedicated to that tenant
|
||||
3. **Environment reuse** - Subsequent requests from the same tenant reuse the same environment (warm start)
|
||||
4. **Isolation guarantee** - Execution environments are never shared between different tenants
|
||||
5. **Data persistence** - Tenant data persists in memory across invocations within the same execution environment
|
||||
|
||||
## Important Considerations
|
||||
|
||||
### Concurrency and Scaling
|
||||
|
||||
- Lambda imposes a limit of **2,500 tenant-isolated execution environments** (active or idle) for every 1,000 concurrent executions
|
||||
- Each tenant can scale independently based on their request volume
|
||||
- Cold starts occur more frequently due to tenant-specific environments
|
||||
|
||||
### Pricing
|
||||
|
||||
- Standard Lambda pricing applies (compute time and requests)
|
||||
- **Additional charge** when Lambda creates a new tenant-isolated execution environment
|
||||
- Price depends on allocated memory and CPU architecture
|
||||
- See [AWS Lambda Pricing](https://aws.amazon.com/lambda/pricing) for details
|
||||
|
||||
### Limitations
|
||||
|
||||
Tenant isolation mode is **not supported** with:
|
||||
- Function URLs
|
||||
- Provisioned concurrency
|
||||
- SnapStart
|
||||
|
||||
### Supported Invocation Methods
|
||||
|
||||
- ✅ Synchronous invocations
|
||||
- ✅ Asynchronous invocations
|
||||
- ✅ API Gateway event triggers
|
||||
- ✅ AWS SDK invocations
|
||||
|
||||
## Security Best Practices
|
||||
|
||||
1. **Execution role applies to all tenants** - Use IAM policies to restrict access to tenant-specific resources
|
||||
2. **Validate tenant identifiers** - Ensure tenant IDs are properly authenticated and authorized
|
||||
3. **Implement tenant-aware logging** - Include tenant ID in CloudWatch logs for audit trails
|
||||
4. **Set appropriate timeouts** - Configure function timeout based on expected workload
|
||||
5. **Monitor per-tenant metrics** - Use CloudWatch to track invocations, errors, and duration per tenant
|
||||
|
||||
## Monitoring
|
||||
|
||||
### CloudWatch Metrics
|
||||
|
||||
Lambda automatically publishes metrics with tenant dimensions:
|
||||
|
||||
- `Invocations` - Number of invocations per tenant
|
||||
- `Duration` - Execution time per tenant
|
||||
- `Errors` - Error count per tenant
|
||||
- `Throttles` - Throttled requests per tenant
|
||||
|
||||
### Accessing Metrics
|
||||
|
||||
```bash
|
||||
# Get invocation count for a specific tenant
|
||||
aws cloudwatch get-metric-statistics \
|
||||
--namespace AWS/Lambda \
|
||||
--metric-name Invocations \
|
||||
--dimensions Name=FunctionName,Value=MultiTenant Name=TenantId,Value=alice \
|
||||
--start-time 2024-01-15T00:00:00Z \
|
||||
--end-time 2024-01-15T23:59:59Z \
|
||||
--period 3600 \
|
||||
--statistics Sum
|
||||
```
|
||||
|
||||
## Learn More
|
||||
|
||||
- [AWS Lambda Tenant Isolation Documentation](https://docs.aws.amazon.com/lambda/latest/dg/tenant-isolation.html)
|
||||
- [Configuring Tenant Isolation](https://docs.aws.amazon.com/lambda/latest/dg/tenant-isolation-configure.html)
|
||||
- [Invoking Tenant-Isolated Functions](https://docs.aws.amazon.com/lambda/latest/dg/tenant-isolation-invoke.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/)
|
||||
- [Swift AWS Lambda Runtime](https://github.com/swift-server/swift-aws-lambda-runtime)
|
||||
|
||||
## License
|
||||
|
||||
This example is part of the Swift AWS Lambda Runtime project and is licensed under Apache License 2.0.
|
||||
@@ -0,0 +1,107 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This source file is part of the SwiftAWSLambdaRuntime open source project
|
||||
//
|
||||
// Copyright SwiftAWSLambdaRuntime project authors
|
||||
// Copyright (c) Amazon.com, Inc. or its affiliates.
|
||||
// Licensed under Apache License v2.0
|
||||
//
|
||||
// See LICENSE.txt for license information
|
||||
// See CONTRIBUTORS.txt for the list of SwiftAWSLambdaRuntime project authors
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
import AWSLambdaEvents
|
||||
import AWSLambdaRuntime
|
||||
|
||||
#if canImport(FoundationEssentials)
|
||||
import FoundationEssentials
|
||||
#else
|
||||
import Foundation
|
||||
#endif
|
||||
|
||||
let tenants = TenantDataStore()
|
||||
|
||||
let runtime = LambdaRuntime {
|
||||
(event: APIGatewayRequest, context: LambdaContext) -> APIGatewayResponse in
|
||||
|
||||
// Extract tenant ID from context
|
||||
guard let tenantID = context.tenantID else {
|
||||
return APIGatewayResponse(statusCode: .badRequest, body: "No Tenant ID provided")
|
||||
}
|
||||
|
||||
// Get or create tenant data
|
||||
let currentData = await tenants[tenantID] ?? TenantData(tenantID: tenantID)
|
||||
|
||||
// Add new request
|
||||
let updatedData = currentData.addingRequest()
|
||||
|
||||
// Store updated data
|
||||
await tenants.update(id: tenantID, data: updatedData)
|
||||
|
||||
return try APIGatewayResponse(statusCode: .ok, encodableBody: updatedData)
|
||||
}
|
||||
|
||||
try await runtime.run()
|
||||
|
||||
actor TenantDataStore {
|
||||
private var tenants: [String: TenantData] = [:]
|
||||
|
||||
subscript(id: String) -> TenantData? {
|
||||
tenants[id]
|
||||
}
|
||||
|
||||
// subscript setters can't be called from outside of the actor
|
||||
func update(id: String, data: TenantData) {
|
||||
tenants[id] = data
|
||||
}
|
||||
}
|
||||
|
||||
struct TenantData: Codable {
|
||||
struct TenantRequest: Codable {
|
||||
let requestNumber: Int
|
||||
let timestamp: String
|
||||
}
|
||||
|
||||
let tenantID: String
|
||||
let requestCount: Int
|
||||
let firstRequest: String
|
||||
let requests: [TenantRequest]
|
||||
|
||||
init(tenantID: String) {
|
||||
self.init(
|
||||
tenantID: tenantID,
|
||||
requestCount: 0,
|
||||
firstRequest: "\(Date().timeIntervalSince1970)",
|
||||
requests: []
|
||||
)
|
||||
}
|
||||
|
||||
func addingRequest() -> TenantData {
|
||||
let newCount = requestCount + 1
|
||||
let newRequest = TenantRequest(
|
||||
requestNumber: newCount,
|
||||
timestamp: "\(Date().timeIntervalSince1970)"
|
||||
)
|
||||
return TenantData(
|
||||
tenantID: tenantID,
|
||||
requestCount: newCount,
|
||||
firstRequest: firstRequest,
|
||||
requests: requests + [newRequest]
|
||||
)
|
||||
}
|
||||
|
||||
private init(
|
||||
tenantID: String,
|
||||
requestCount: Int,
|
||||
firstRequest: String,
|
||||
requests: [TenantRequest]
|
||||
) {
|
||||
self.tenantID = tenantID
|
||||
self.requestCount = requestCount
|
||||
self.firstRequest = firstRequest
|
||||
self.requests = requests
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,121 @@
|
||||
AWSTemplateFormatVersion: '2010-09-09'
|
||||
Transform: AWS::Serverless-2016-10-31
|
||||
Description: SAM Template for Multi Tenant Lambda Example
|
||||
|
||||
# This is an example SAM template for the purpose of this project.
|
||||
# When deploying such infrastructure in production environment,
|
||||
# we strongly encourage you to follow these best practices for improved security and resiliency
|
||||
# - Enable access logging on API Gateway
|
||||
# See: https://docs.aws.amazon.com/apigateway/latest/developerguide/set-up-logging.html)
|
||||
# - Ensure that AWS Lambda function is configured for function-level concurrent execution limit
|
||||
# See: https://docs.aws.amazon.com/lambda/latest/dg/lambda-concurrency.html
|
||||
# https://docs.aws.amazon.com/lambda/latest/dg/configuration-concurrency.html
|
||||
# - Check encryption settings for Lambda environment variable
|
||||
# See: https://docs.aws.amazon.com/lambda/latest/dg/configuration-envvars-encryption.html
|
||||
# - Ensure that AWS Lambda function is configured for a Dead Letter Queue(DLQ)
|
||||
# See: https://docs.aws.amazon.com/lambda/latest/dg/invocation-async-retain-records.html#invocation-dlq
|
||||
# - Ensure that AWS Lambda function is configured inside a VPC when it needs to access private resources
|
||||
# See: https://docs.aws.amazon.com/lambda/latest/dg/configuration-vpc.html
|
||||
# Code Example: https://github.com/awslabs/swift-aws-lambda-runtime/tree/main/Examples/ServiceLifecycle%2BPostgres
|
||||
|
||||
Resources:
|
||||
# API Gateway REST API
|
||||
MultiTenantApi:
|
||||
Type: AWS::Serverless::Api
|
||||
Properties:
|
||||
StageName: Prod
|
||||
DefinitionBody:
|
||||
openapi: 3.0.1
|
||||
info:
|
||||
title: MultiTenant API
|
||||
version: 1.0.0
|
||||
paths:
|
||||
/{proxy+}:
|
||||
x-amazon-apigateway-any-method:
|
||||
parameters:
|
||||
- name: tenant-id
|
||||
in: query
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
- name: proxy
|
||||
in: path
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
x-amazon-apigateway-request-validator: params-only
|
||||
x-amazon-apigateway-integration:
|
||||
type: aws_proxy
|
||||
httpMethod: POST
|
||||
uri: !Sub arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${MultiTenantLambda.Arn}/invocations
|
||||
requestParameters:
|
||||
integration.request.header.X-Amz-Tenant-Id: method.request.querystring.tenant-id
|
||||
/:
|
||||
x-amazon-apigateway-any-method:
|
||||
parameters:
|
||||
- name: tenant-id
|
||||
in: query
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
x-amazon-apigateway-request-validator: params-only
|
||||
x-amazon-apigateway-integration:
|
||||
type: aws_proxy
|
||||
httpMethod: POST
|
||||
uri: !Sub arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${MultiTenantLambda.Arn}/invocations
|
||||
requestParameters:
|
||||
integration.request.header.X-Amz-Tenant-Id: method.request.querystring.tenant-id
|
||||
x-amazon-apigateway-request-validators:
|
||||
params-only:
|
||||
validateRequestParameters: true
|
||||
validateRequestBody: false
|
||||
|
||||
# Lambda function
|
||||
MultiTenantLambda:
|
||||
Type: AWS::Serverless::Function
|
||||
Properties:
|
||||
CodeUri: .build/plugins/AWSLambdaPackager/outputs/AWSLambdaPackager/MultiTenant/MultiTenant.zip
|
||||
Timeout: 60
|
||||
Handler: swift.bootstrap # ignored by the Swift runtime
|
||||
Runtime: provided.al2023
|
||||
MemorySize: 128
|
||||
Architectures:
|
||||
- arm64
|
||||
# https://docs.aws.amazon.com/lambda/latest/dg/tenant-isolation-configure.html#tenant-isolation-cfn
|
||||
TenancyConfig:
|
||||
TenantIsolationMode: PER_TENANT
|
||||
Environment:
|
||||
Variables:
|
||||
# by default, AWS Lambda runtime produces no log
|
||||
# use `LOG_LEVEL: debug` for lifecycle and event handling information
|
||||
# use `LOG_LEVEL: trace` for detailed input event information
|
||||
LOG_LEVEL: trace
|
||||
Events:
|
||||
RootPath:
|
||||
Type: Api
|
||||
Properties:
|
||||
RestApiId: !Ref MultiTenantApi
|
||||
Path: /
|
||||
Method: ANY
|
||||
ProxyPath:
|
||||
Type: Api
|
||||
Properties:
|
||||
RestApiId: !Ref MultiTenantApi
|
||||
Path: /{proxy+}
|
||||
Method: ANY
|
||||
|
||||
# Permission for API Gateway to invoke Lambda
|
||||
MultiTenantLambdaPermission:
|
||||
Type: AWS::Lambda::Permission
|
||||
Properties:
|
||||
FunctionName: !Ref MultiTenantLambda
|
||||
Action: lambda:InvokeFunction
|
||||
Principal: apigateway.amazonaws.com
|
||||
SourceArn: !Sub arn:aws:execute-api:${AWS::Region}:${AWS::AccountId}:${MultiTenantApi}/*/*
|
||||
|
||||
Outputs:
|
||||
# print API Gateway endpoint
|
||||
APIGatewayEndpoint:
|
||||
Description: API Gateway endpoint URL
|
||||
# https://docs.aws.amazon.com/lambda/latest/dg/tenant-isolation-invoke.html#tenant-isolation-invoke-apigateway
|
||||
Value: !Sub "https://${MultiTenantApi}.execute-api.${AWS::Region}.amazonaws.com/Prod?tenant-id=seb"
|
||||
Reference in New Issue
Block a user