Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 61 additions & 0 deletions sqs-lambda-tenant-isolation-sam-py/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
# Lambda Tenant Isolation with SQS

This pattern demonstrate AWS Lambda's tenant isolation feature in Multi-tenant application. It uses single SQS for multi-tenant applucation and isolating messages using messagegroupid and invoking isolated lambda enviornments.

## Key Features

- Tenant isolation at infrastructure level (no custom routing logic)
- Execution environments never shared between tenants
- Asynchronous invocation pattern
- Automatic tenant context propagation

Learn more about this pattern at [Serverless Land Patterns](https://serverlessland.com/patterns/sqs-lambda-tenant-isolation)

Important: this application uses various AWS services and there are costs associated with these services after the Free Tier usage - please see the [AWS Pricing page](https://aws.amazon.com/pricing/) for details. You are responsible for any AWS costs incurred. No warranty is implied in this example.


## Architecture

```
SQS Queue → SQS Processor Lambda → Tenant-Isolated Lambda
(reads customer-id) (processes with tenant isolation)
```

## Components

### 1. SQS Processor (`sqs-processor/`)
- Triggered by SQS queue messages
- Extracts `customer-id` from message payload
- Invokes tenant-isolated Lambda asynchronously with `TenantId` parameter

### 2. Tenant-Isolated Processor (`tenant-isolated-processor/`)
- Configured with tenant isolation mode enabled
- Processes requests in isolated execution environments per tenant
- Accesses tenant ID via `context.identity.tenant_id`

## Message Format

```json
{
"data": "your payload here"
}
```

## Deployment

```bash
sam build
sam deploy --guided
```

## Testing

Send a message to the SQS queue:

```bash
aws sqs send-message \
--queue-url <QUEUE_URL> \
--message-body '{"data": "test payload"}'
```


49 changes: 49 additions & 0 deletions sqs-lambda-tenant-isolation-sam-py/example-pattern.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
{
"title": "AWS Lambda Tenant Isolation with SQS",
"description": "Lambda Isolation feature",
"language": "",
"level": "200",
"framework": "AWS SAM",
"introBox": {
"headline": "How it works",
"text": [
"This pattern demonstrate AWS Lambda's tenant isolation feature in Multi-tenant application."
]
},
"gitHub": {
"template": {
"repoURL": "https://github.com/aws-samples/serverless-patterns/tree/main/sqs-lambda-tenant-isolation-sam-py",
"templateURL": "serverless-patterns/sqs-lambda-tenant-isolation-sam-py",
"projectFolder": "sqs-lambda-tenant-isolation-sam-py"
}
},
"resources": {
"bullets": [{
"text": "AWS Lambda tenant isolation",
"link": "https://docs.aws.amazon.com/lambda/latest/dg/tenant-isolation.html"
}
]
},
"deploy": {
"text": ["sam build", "sam deploy --guided"]
},
"testing": {
"text": ["See the GitHub repo for detailed testing instructions."]
},
"cleanup": {
"text": ["Delete the stack: <code>sam delete</code>."]
},
"authors": [{
"name": "Mitesh Purohit",
"image": "",
"bio": "Sr Solution Architect, AWS",
"linkedin": "https://www.linkedin.com/in/mitup/"
},
{
"name": "Ricardo Marques",
"image": "",
"bio": "Sr Serverless Specialist, AWS",
"linkedin": "https://www.linkedin.com/in/ricardo-marques-aws/"
}
]
}
29 changes: 29 additions & 0 deletions sqs-lambda-tenant-isolation-sam-py/sqs-processor/index.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import json
import boto3
import os

lambda_client = boto3.client('lambda')
TENANT_ISOLATED_FUNCTION = os.environ['TENANT_ISOLATED_FUNCTION_NAME']

def handler(event, context):
for record in event['Records']:
body = json.loads(record['body'])

# Get message group ID from SQS attributes
attributes = record.get('attributes') or {}
message_group_id = attributes.get('MessageGroupId')

if not message_group_id:
print(f"Missing MessageGroupId in SQS record: {record}")
message_group_id = "default"

lambda_client.invoke(
FunctionName=TENANT_ISOLATED_FUNCTION,
InvocationType='Event',
Payload=json.dumps(body),
TenantId=message_group_id
)

print(f"Invoked tenant-isolated function for messagegroup: {message_group_id}")

return {'statusCode': 200}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
boto3>=1.26.0
69 changes: 69 additions & 0 deletions sqs-lambda-tenant-isolation-sam-py/template.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: Lambda Tenant Isolation Demo

Resources:
ProcessingQueue:
Type: AWS::SQS::Queue
Properties:
QueueName: tenant-isolation-queue
VisibilityTimeout: 300

TenantIsolatedFunctionRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Principal:
Service: lambda.amazonaws.com
Action: sts:AssumeRole
ManagedPolicyArns:
- arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole

TenantIsolatedFunction:
Type: AWS::Serverless::Function
Properties:
FunctionName: tenant-isolated-processor
CodeUri: tenant-isolated-processor/
Handler: index.handler
Runtime: python3.12
Timeout: 120
Role: !GetAtt TenantIsolatedFunctionRole.Arn
TenancyConfig:
TenantIsolationMode: PER_TENANT

SQSProcessorFunction:
Type: AWS::Serverless::Function
Properties:
FunctionName: sqs-processor
CodeUri: sqs-processor/
Handler: index.handler
Runtime: python3.12
Timeout: 60
Environment:
Variables:
TENANT_ISOLATED_FUNCTION_NAME: !Ref TenantIsolatedFunction
Policies:
- Statement:
- Effect: Allow
Action:
- lambda:InvokeFunction
Resource: !GetAtt TenantIsolatedFunction.Arn
Events:
SQSEvent:
Type: SQS
Properties:
Queue: !GetAtt ProcessingQueue.Arn
BatchSize: 10

Outputs:
QueueUrl:
Value: !Ref ProcessingQueue
QueueArn:
Value: !GetAtt ProcessingQueue.Arn
TenantIsolatedFunctionArn:
Value: !GetAtt TenantIsolatedFunction.Arn
SQSProcessorFunctionArn:
Value: !GetAtt SQSProcessorFunction.Arn
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import json

def handler(event, context):
tenant_id = context.tenant_id

print(f"Processing request for tenant: {tenant_id}")
print(f"Event data: {json.dumps(event)}")

# Process tenant-specific logic here
result = {
'tenant_id': tenant_id,
'message': 'Request processed successfully',
'data': event
}

return result
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# No external dependencies required