Skip to content

Commit 5a22971

Browse files
authored
Merge pull request #2968 from devops-arch-cloud/sqs-lambda-tenant-isolation-sam-py
SQL Lambda Tenant Isolation
2 parents 30e8b65 + 49c7ed4 commit 5a22971

7 files changed

Lines changed: 352 additions & 0 deletions

File tree

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
# AWS Lambda Tenant Isolation with Amazon SQS
2+
3+
This pattern demonstrates AWS Lambda's tenant isolation feature in Multi-tenant application. It uses single Amazon SQS for multi-tenant application and isolating messages using MessageGroupId and invoking isolated AWS Lambda environments.
4+
5+
## Key Features
6+
7+
- Tenant isolation at infrastructure level (no custom routing logic)
8+
- Execution environments never shared between tenants
9+
- Asynchronous invocation pattern
10+
- Automatic tenant context propagation
11+
12+
Learn more about this pattern at [Serverless Land Patterns](https://serverlessland.com/patterns/sqs-lambda-tenant-isolation-sam-py)
13+
14+
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.
15+
16+
## Requirements
17+
18+
* [Create an AWS account](https://portal.aws.amazon.com/gp/aws/developer/registration/index.html) if you do not already have one and log in. The IAM user that you use must have sufficient permissions to make necessary AWS service calls and manage AWS resources.
19+
* [AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/install-cliv2.html) installed and configured
20+
* [Git Installed](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git)
21+
* [AWS Serverless Application Model](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-cli-install.html) (AWS SAM) installed
22+
23+
## How it works
24+
25+
<img width="535" height="183" alt="image" src="https://github.com/user-attachments/assets/b6af3efa-e81b-4a08-80ca-b7536934d490" />
26+
27+
### 1. SQS Processor (`sqs-processor/`)
28+
- Triggered by SQS queue messages
29+
- Invokes tenant-isolated Lambda asynchronously
30+
31+
### 2. Tenant-Isolated Processor (`tenant-isolated-processor/`)
32+
- Configured with tenant isolation mode enabled
33+
- Processes requests in isolated execution environments per tenant using message-group-id
34+
35+
## Message Format
36+
37+
```json
38+
{
39+
"data": "your payload here"
40+
}
41+
```
42+
43+
## Deployment Instructions
44+
45+
```bash
46+
sam build
47+
sam deploy --guided
48+
```
49+
50+
## Testing
51+
52+
Step 1:
53+
After deploying infrastructure using SAM, run below command to get SQS Queue URL. Replace <your-stack-name> with your cloudformation stack name.
54+
55+
```bash
56+
aws cloudformation describe-stacks \
57+
--stack-name <your-stack-name> \
58+
--query "Stacks[0].Outputs[?OutputKey=='MyQueueUrl'].OutputValue" \
59+
--output text
60+
```
61+
62+
Step 2:
63+
You send messages to the SQS queue with --message-group-id set to a tenant identifier. Use below CLI command to send-message. Make sure to set --message-group-id as tenant name. Send multiple messages with different tenant name
64+
65+
```bash
66+
aws sqs send-message \
67+
--queue-url <QUEUE_URL> \
68+
--message-body '{"data": "test payload"}' \
69+
--message-group-id "tenant-blue"
70+
```
71+
72+
```bash
73+
aws sqs send-message \
74+
--queue-url <QUEUE_URL> \
75+
--message-body '{"data": "test payload"}' \
76+
--message-group-id "tenant-green"
77+
```
78+
79+
Step 3:
80+
The SQS processor Lambda picks up the message, reads the MessageGroupId from the SQS record attributes, and passes it as the TenantId when invoking the tenant-isolated LambdaAfter dropping the message, review cloudwatch log for Tenant-Isolated Lambda.
81+
82+
```bash
83+
aws logs describe-log-streams \
84+
--log-group-name /aws/lambda/tenant-isolated-processor \
85+
--order-by LastEventTime \
86+
--descending
87+
```
88+
89+
Different log streams should be created for each tenant.
90+
91+
## Cleanup
92+
93+
Delete the stack
94+
95+
```bash
96+
sam delete
97+
```
98+
----
99+
Copyright 2026 Amazon.com, Inc. or its affiliates. All Rights Reserved.
100+
101+
SPDX-License-Identifier: MIT-0
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
{
2+
"title": "AWS Lambda Tenant Isolation with Amazon SQS",
3+
"description": "This pattern demonstrates AWS Lambda tenant isolation with Amazon SQS fair queues for multi-tenant message processing."
4+
"language": "",
5+
"level": "200",
6+
"framework": "AWS SAM",
7+
"introBox": {
8+
"headline": "How it works",
9+
"text": [
10+
"This pattern demonstrate AWS Lambda's tenant isolation feature in Multi-tenant application."
11+
]
12+
},
13+
"gitHub": {
14+
"template": {
15+
"repoURL": "https://github.com/aws-samples/serverless-patterns/tree/main/sqs-lambda-tenant-isolation-sam-py",
16+
"templateURL": "serverless-patterns/sqs-lambda-tenant-isolation-sam-py",
17+
"projectFolder": "sqs-lambda-tenant-isolation-sam-py"
18+
}
19+
},
20+
"resources": {
21+
"bullets": [{
22+
"text": "AWS Lambda tenant isolation",
23+
"link": "https://docs.aws.amazon.com/lambda/latest/dg/tenant-isolation.html"
24+
}
25+
]
26+
},
27+
"deploy": {
28+
"text": ["sam build", "sam deploy --guided"]
29+
},
30+
"testing": {
31+
"text": ["See the GitHub repo for detailed testing instructions."]
32+
},
33+
"cleanup": {
34+
"text": ["Delete the stack: <code>sam delete</code>."]
35+
},
36+
"authors": [{
37+
"name": "Mitesh Purohit",
38+
"image": "",
39+
"bio": "Sr Solution Architect, AWS",
40+
"linkedin": "https://www.linkedin.com/in/mitup/"
41+
},
42+
{
43+
"name": "Ricardo Marques",
44+
"image": "",
45+
"bio": "Sr Serverless Specialist, AWS",
46+
"linkedin": "https://www.linkedin.com/in/ricardo-marques-aws/"
47+
}
48+
]
49+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import json
2+
import boto3
3+
import os
4+
5+
lambda_client = boto3.client('lambda')
6+
TENANT_ISOLATED_FUNCTION = os.environ['TENANT_ISOLATED_FUNCTION_NAME']
7+
8+
def handler(event, context):
9+
for record in event['Records']:
10+
body = json.loads(record['body'])
11+
12+
# Get message group ID from SQS attributes
13+
attributes = record.get('attributes') or {}
14+
message_group_id = attributes.get('MessageGroupId')
15+
16+
if not message_group_id:
17+
print(f"Missing MessageGroupId in SQS record: {record}")
18+
message_group_id = "default"
19+
20+
lambda_client.invoke(
21+
FunctionName=TENANT_ISOLATED_FUNCTION,
22+
InvocationType='Event',
23+
Payload=json.dumps(body),
24+
TenantId=message_group_id
25+
)
26+
27+
print(f"Invoked tenant-isolated function for messagegroup: {message_group_id}")
28+
29+
return {'statusCode': 200}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
boto3>=1.35.0
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
AWSTemplateFormatVersion: '2010-09-09'
2+
Transform: AWS::Serverless-2016-10-31
3+
Description: Lambda Tenant Isolation Demo
4+
5+
Resources:
6+
ProcessingQueue:
7+
Type: AWS::SQS::Queue
8+
Properties:
9+
QueueName: tenant-isolation-queue
10+
VisibilityTimeout: 300
11+
12+
TenantIsolatedFunctionRole:
13+
Type: AWS::IAM::Role
14+
Properties:
15+
AssumeRolePolicyDocument:
16+
Version: '2012-10-17'
17+
Statement:
18+
- Effect: Allow
19+
Principal:
20+
Service: lambda.amazonaws.com
21+
Action: sts:AssumeRole
22+
ManagedPolicyArns:
23+
- arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
24+
25+
TenantIsolatedFunction:
26+
Type: AWS::Serverless::Function
27+
Properties:
28+
FunctionName: tenant-isolated-processor
29+
CodeUri: tenant-isolated-processor/
30+
Handler: index.handler
31+
Runtime: python3.12
32+
Timeout: 120
33+
Role: !GetAtt TenantIsolatedFunctionRole.Arn
34+
TenancyConfig:
35+
TenantIsolationMode: PER_TENANT
36+
37+
SQSProcessorFunction:
38+
Type: AWS::Serverless::Function
39+
Properties:
40+
FunctionName: sqs-processor
41+
CodeUri: sqs-processor/
42+
Handler: index.handler
43+
Runtime: python3.12
44+
Timeout: 60
45+
Environment:
46+
Variables:
47+
TENANT_ISOLATED_FUNCTION_NAME: !Ref TenantIsolatedFunction
48+
Policies:
49+
- Statement:
50+
- Effect: Allow
51+
Action:
52+
- lambda:InvokeFunction
53+
Resource: !GetAtt TenantIsolatedFunction.Arn
54+
Events:
55+
SQSEvent:
56+
Type: SQS
57+
Properties:
58+
Queue: !GetAtt ProcessingQueue.Arn
59+
BatchSize: 10
60+
61+
Outputs:
62+
QueueUrl:
63+
Value: !Ref ProcessingQueue
64+
QueueArn:
65+
Value: !GetAtt ProcessingQueue.Arn
66+
TenantIsolatedFunctionArn:
67+
Value: !GetAtt TenantIsolatedFunction.Arn
68+
SQSProcessorFunctionArn:
69+
Value: !GetAtt SQSProcessorFunction.Arn
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import json
2+
3+
def handler(event, context):
4+
tenant_id = context.tenant_id
5+
6+
print(f"Processing request for tenant: {tenant_id}")
7+
print(f"Event data: {json.dumps(event)}")
8+
9+
# Process tenant-specific logic here
10+
result = {
11+
'tenant_id': tenant_id,
12+
'message': 'Request processed successfully',
13+
'data': event
14+
}
15+
16+
return result
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
{
2+
"title": "AWS Lambda Tenant Isolation with Amazon SQS",
3+
"description": "This pattern demonstrates AWS Lambda tenant isolation with Amazon SQS fair queues for multi-tenant message processing.",
4+
"level": "200",
5+
"framework": "AWS SAM",
6+
"language": "Python",
7+
"introBox": {
8+
"headline": "How it works",
9+
"text": [
10+
"This pattern demonstrate AWS Lambda's tenant isolation feature in Multi-tenant application."
11+
]
12+
},
13+
"gitHub": {
14+
"template": {
15+
"repoURL": "https://github.com/aws-samples/serverless-patterns/tree/main/sqs-lambda-tenant-isolation-sam-py",
16+
"templateURL": "serverless-patterns/sqs-lambda-tenant-isolation-sam-py",
17+
"projectFolder": "sqs-lambda-tenant-isolation-sam-py",
18+
"templateFile": "template.yml"
19+
}
20+
},
21+
"resources": {
22+
"bullets": [
23+
{
24+
"text": "AWS Lambda tenant isolation",
25+
"link": "https://docs.aws.amazon.com/lambda/latest/dg/tenant-isolation.html"
26+
}
27+
]
28+
},
29+
"deploy": {
30+
"text": [
31+
"sam build",
32+
"sam deploy --guided"
33+
]
34+
},
35+
"testing": {
36+
"text": [
37+
"See the GitHub repo for detailed testing instructions."
38+
]
39+
},
40+
"cleanup": {
41+
"text": [
42+
"Delete the stack: <code>sam delete</code>."
43+
]
44+
},
45+
"authors": [
46+
{
47+
"name": "Mitesh Purohit",
48+
"image": "",
49+
"bio": "Sr Solution Architect, AWS",
50+
"linkedin": "in/mitup/"
51+
},
52+
{
53+
"name": "Ricardo Marques",
54+
"image": "",
55+
"bio": "Sr Serverless Specialist, AWS",
56+
"linkedin": "in/ricardo-marques-aws/"
57+
}
58+
],
59+
"patternArch": {
60+
"icon1": {
61+
"x": 20,
62+
"y": 50,
63+
"service": "sqs",
64+
"label": "Amazon SQS"
65+
},
66+
"icon2": {
67+
"x": 50,
68+
"y": 50,
69+
"service": "lambda",
70+
"label": "SQS Processor"
71+
},
72+
"icon3": {
73+
"x": 80,
74+
"y": 50,
75+
"service": "lambda",
76+
"label": "Tenant Isolated"
77+
},
78+
"line1": {
79+
"from": "icon1",
80+
"to": "icon2"
81+
},
82+
"line2": {
83+
"from": "icon2",
84+
"to": "icon3"
85+
}
86+
}
87+
}

0 commit comments

Comments
 (0)