How to connect multiple Lambda functions in one API Gateway (SAM Template) with Stage variables
Learn how to connect multiple AWS Lambda functions to a single API Gateway using a SAM (Serverless Application Model) template. This guide will show you how to configure your Lambda functions, set up API Gateway integrations, and manage different environments with stages for a streamlined serverless setup.
Prerequisites:
- AWS CLI: Installed and configured on your local machine.
- AWS SAM CLI: Installed and configured on your local machine.
- AWS Account: An active AWS account with permissions to create and manage Lambda functions and API Gateway resources.
- IAM Roles: Basic understanding of AWS IAM roles and permissions required for Lambda and API Gateway.
- Basic YAML Knowledge: Understanding of YAML for writing SAM templates.
Configuration
For deploying serverless applications with AWS SAM CLI we need a policy. This policy includes actions for creating, updating, and deleting these resources while also enabling S3 operations for deployment artifacts and CloudWatch Logs for function monitoring.
Let’s create a policy and role for SAM CLI with the least privileges practice.
- Go to IAM Dashboard:
- In the AWS Management Console, select IAM.
2. Create Policy:
- Click Policies on the left menu, then Create Policy.
- Choose the JSON tab and paste the IAM policy JSON.
- SAM-CLI-Least-Privilege-Policy
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"cloudformation:CreateStack",
"cloudformation:UpdateStack",
"cloudformation:DeleteStack",
"cloudformation:DescribeStackResources",
"cloudformation:DescribeStacks",
"cloudformation:GetTemplate",
"cloudformation:ExecuteChangeSet",
"cloudformation:DescribeStackEvents",
"cloudformation:CreateChangeSet",
"cloudformation:DescribeChangeSet",
"cloudformation:ListStackResources",
"cloudformation:ListStacks",
"cloudformation:GetTemplateSummary",
"apigateway:POST",
"apigateway:PUT",
"apigateway:DELETE",
"apigateway:GET",
"apigateway:PATCH",
"s3:CreateBucket",
"s3:PutObject",
"s3:GetObject",
"s3:ListBucket",
"s3:DeleteObject",
"logs:CreateLogGroup",
"logs:CreateLogStream",
"logs:PutLogEvents",
"logs:DescribeLogGroups",
"logs:DescribeLogStreams",
"iam:CreateRole",
"iam:DetachRolePolicy",
"iam:DeleteRole",
"iam:TagRole",
"iam:AttachRolePolicy",
"iam:GetRole",
"lambda:*"
],
"Resource": "*"
},
{
"Effect": "Allow",
"Action": [
"iam:PassRole"
],
"Resource": "*"
}
]
}
3. Attach Policy to a User:
- Go to Users in the IAM dashboard.
- Select the user to which you want to attach the policy.
- Click Add Permissions.
- Choose Attach policies directly.
- Search for and select the policy you created.
- Click Next: Review, then click Add Permissions.
This process ensures that your IAM user has the correct permissions to use AWS SAM CLI.
Project set up
After successfully configuring your permission and SAM CLI, we can move on to setting up our project.
In your Unix Shell
- Check if SAM CLI is Available first
sam --version
Expected output
SAM CLI, version <your version>
If it shows an error it means SAM CLI is not installed on your machine please refer here to how to install SAM CLI
2. Initilized your application with
sam init
Follow the guide with these inputs
Source: AWS Quick Start Templates
Template: Hello World Example
Runtime: nodejs20.x
Package Type: Zip
Typescipt: Yes
XRay Tracing: No
Enable Cloudwatch Insigh: Yes
Structured Logging JSON: No
Project Name: sam-multiple-lambda
Function Duplication
To have multiple lambda we first need to duplicate our functions into two for now.
- Let’s go to our project directory.
cd sam-multiple-lambda
2. We then rename our function repo to “func1” and duplicate the repo name “func2”
mv hello-world func1 && cp -r func1 func2
3. (Optional) If you want to test locally don't forget to install dependencies on each function directory with
npm install .
SAM YAML Modification
This YAML is designed to manage deployment stages and integrate API Gateway with Lambda functions using different aliases based on the environment. We will break down each template section to understand its purpose and configuration.
Here’s the complete SAM template that we will be discussing:
AWSTemplateFormatVersion: "2010-09-09"
Transform: AWS::Serverless-2016-10-31
Description: >
func1-and-func2-sam
Sample SAM Template for func1 and func2
Parameters:
Stage:
Type: String
AllowedValues:
- dev
- prod
Description: Deployment stage (dev or prod)
Conditions:
IsDev:
Fn::Equals: [!Ref Stage, "dev"]
IsProd:
Fn::Equals: [!Ref Stage, "prod"]
Globals:
Function:
Timeout: 3
Resources:
# API GATEWAY Config
ApiGatewayApi:
Type: AWS::Serverless::Api
DependsOn: Func1Function
Properties:
StageName: !Ref Stage
Cors: "'*'"
Variables:
alias: !Ref Stage
DefinitionBody:
openapi: "3.0.1"
paths:
/func1:
get:
x-amazon-apigateway-integration:
uri: !Sub "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${Func1Function.Arn}:${Stage}/invocations"
passthroughBehavior: "when_no_match"
httpMethod: POST
type: "aws_proxy"
/func2:
get:
x-amazon-apigateway-integration:
uri: !Sub "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${Func2Function.Arn}:${Stage}/invocations"
passthroughBehavior: "when_no_match"
httpMethod: POST
type: "aws_proxy"
# Function 1
Func1Function:
Type: AWS::Serverless::Function
Properties:
CodeUri: func1/
Handler: app.lambdaHandler
Runtime: nodejs20.x
Architectures:
- x86_64
Metadata:
BuildMethod: esbuild
BuildProperties:
Minify: true
Target: "es2020"
Sourcemap: true
EntryPoints:
- app.ts
Func1FunctionVersion:
Type: AWS::Lambda::Version
Properties:
FunctionName: !Ref Func1Function
Description: "Version 1.0.0 of Func1Function"
# Alias if Development
Func1FunctionAliasProd:
Type: AWS::Lambda::Alias
Condition: IsDev
Properties:
FunctionName: !Ref Func1Function
FunctionVersion: $LATEST
Name: !Sub "${Stage}"
# Alias if Production
Func1FunctionAliasDev:
Type: AWS::Lambda::Alias
Condition: IsProd
Properties:
FunctionName: !Ref Func1Function
FunctionVersion: !Ref Func1FunctionVersion
Name: !Sub "${Stage}"
# Function 2
Func2Function:
Type: AWS::Serverless::Function
Properties:
CodeUri: func2/
Handler: app.lambdaHandler
Runtime: nodejs20.x
Architectures:
- x86_64
Metadata:
BuildMethod: esbuild
BuildProperties:
Minify: true
Target: "es2020"
Sourcemap: true
EntryPoints:
- app.ts
Func2FunctionVersion:
Type: AWS::Lambda::Version
Properties:
FunctionName: !Ref Func2Function
Description: "Version 1.0.0 of Func2Function"
# Alias if Development
Func2FunctionAliasProd:
Type: AWS::Lambda::Alias
Condition: IsDev
Properties:
FunctionName: !Ref Func2Function
FunctionVersion: $LATEST
Name: !Sub "${Stage}"
# Alias if Production
Func2FunctionAliasDev:
Type: AWS::Lambda::Alias
Condition: IsProd
Properties:
FunctionName: !Ref Func2Function
FunctionVersion: !Ref Func2FunctionVersion
Name: !Sub "${Stage}"
# Invoke permissions for Lambda Functions
Func1FunctionInvokePermission:
Type: AWS::Lambda::Permission
Properties:
Action: lambda:InvokeFunction
FunctionName: !Sub "${Func1Function.Arn}:${Stage}"
Principal: apigateway.amazonaws.com
SourceArn: !Sub "arn:aws:execute-api:${AWS::Region}:${AWS::AccountId}:${ApiGatewayApi}/*/*/func1"
Func2FunctionInvokePermission:
Type: AWS::Lambda::Permission
Properties:
Action: lambda:InvokeFunction
FunctionName: !Sub "${Func2Function.Arn}:${Stage}"
Principal: apigateway.amazonaws.com
SourceArn: !Sub "arn:aws:execute-api:${AWS::Region}:${AWS::AccountId}:${ApiGatewayApi}/*/*/func2"
Outputs:
Func1Api:
Description: "API Gateway endpoint URL for Func1"
Value: !Sub "https://${ApiGatewayApi}.execute-api.${AWS::Region}.amazonaws.com/Prod/func1/"
Func2Api:
Description: "API Gateway endpoint URL for Func2"
Value: !Sub "https://${ApiGatewayApi}.execute-api.${AWS::Region}.amazonaws.com/Prod/func2/"
Func1Function:
Description: "Func1 Lambda Function ARN"
Value: !GetAtt Func1Function.Arn
Func2Function:
Description: "Func2 Lambda Function ARN"
Value: !GetAtt Func2Function.Arn
Func1FunctionIamRole:
Description: "Implicit IAM Role created for Func1"
Value: !GetAtt Func1Function.Arn
Func2FunctionIamRole:
Description: "Implicit IAM Role created for Func2"
Value: !GetAtt Func2Function.Arn
Template Breakdown
- Template Header
AWSTemplateFormatVersion: "2010-09-09"
Transform: AWS::Serverless-2016-10-31
Description: >
func1-and-func2-sam
Sample SAM Template for func1 and func2
- AWSTemplateFormatVersion: Specifies the version of AWS CloudFormation used.
- Transform: Indicates that this template uses AWS SAM.
- Description: Provides an overview of the template and its purpose.
2. Parameters
Parameters:
Stage:
Type: String
AllowedValues:
- dev
- prod
Description: Deployment stage (dev or prod)
- Parameters: Defines input parameters for the template.
- Stage: Specifies whether the deployment is for development (
dev
) or production (prod
).
3. Conditions
Conditions:
IsDev:
Fn::Equals: [!Ref Stage, "dev"]
IsProd:
Fn::Equals: [!Ref Stage, "prod"]
- Conditions: Used to control resource creation based on parameter values.
- IsDev: Checks if the
Stage
isdev
. - IsProd: Checks if the
Stage
isprod
.
4. Globals
Globals:
Function:
Timeout: 3
- Globals: Sets default properties for resources.
- Function: Specifies a global timeout of 3 seconds for all Lambda functions.
5. Resources
5.1 API Gateway Configuration
# API GATEWAY Config
ApiGatewayApi:
Type: AWS::Serverless::Api
DependsOn: Func1Function
Properties:
StageName: !Ref Stage
Cors: "'*'"
Variables:
alias: !Ref Stage
DefinitionBody:
openapi: "3.0.1"
paths:
/func1:
get:
x-amazon-apigateway-integration:
uri: !Sub "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${Func1Function.Arn}:${Stage}/invocations"
passthroughBehavior: "when_no_match"
httpMethod: POST
type: "aws_proxy"
/func2:
get:
x-amazon-apigateway-integration:
uri: !Sub "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${Func2Function.Arn}:${Stage}/invocations"
passthroughBehavior: "when_no_match"
httpMethod: POST
type: "aws_proxy"
ApiGatewayApi: Defines an API Gateway resource.
- DependsOn: Ensures
Func1Function
is created before this resource. - StageName: Uses the
Stage
parameter to set the API Gateway stage. - Cors: Configures CORS settings to allow all origins (
'*'
). - Variables: Defines stage-specific variables.
- DefinitionBody: Uses OpenAPI specification to define API endpoints (
/func1
and/func2
) and integrates them with Lambda functions and their aliasesStage
usingaws_proxy
.
5.2 Lambda Function 1
Func1Function:
Type: AWS::Serverless::Function
Properties:
CodeUri: func1/
Handler: app.lambdaHandler
Runtime: nodejs20.x
Architectures:
- x86_64
Metadata:
BuildMethod: esbuild
BuildProperties:
Minify: true
Target: "es2020"
Sourcemap: true
EntryPoints:
- app.ts
Func1Function: Defines the first Lambda function.
- CodeUri: Location of the Lambda function code.
- Handler: Specifies the function handler method.
- Runtime: Sets the Node.js runtime version.
- Architectures: Specifies the Lambda architecture.
- Metadata: Provides build configuration details.
5.3 Lambda Function 1 Version
Func1FunctionVersion:
Type: AWS::Lambda::Version
Properties:
FunctionName: !Ref Func1Function
Description: "Version 1.0.0 of Func1Function"
- Func1FunctionVersion: Defines a version for
Func1Function
.
5.4 Lambda Function 1 Alias
# Alias if Development
Func1FunctionAliasProd:
Type: AWS::Lambda::Alias
Condition: IsDev
Properties:
FunctionName: !Ref Func1Function
FunctionVersion: $LATEST
Name: !Sub "${Stage}"
# Alias if Production
Func1FunctionAliasDev:
Type: AWS::Lambda::Alias
Condition: IsProd
Properties:
FunctionName: !Ref Func1Function
FunctionVersion: !Ref Func1FunctionVersion
Name: !Sub "${Stage}"
- Func1FunctionAliasProd: Creates an alias for
Func1Function
in development, pointing to the$LATEST
version. - Func1FunctionAliasDev: Creates an alias for
Func1Function
in production, pointing to a specific version.
5.5 Lambda Function 2
Func2Function:
Type: AWS::Serverless::Function
Properties:
CodeUri: func2/
Handler: app.lambdaHandler
Runtime: nodejs20.x
Architectures:
- x86_64
Metadata:
BuildMethod: esbuild
BuildProperties:
Minify: true
Target: "es2020"
Sourcemap: true
EntryPoints:
- app.ts
- Func2Function: Defines the second Lambda function with properties similar to
Func1Function
.
5.6 Lambda Function 2 Version
Func2FunctionVersion:
Type: AWS::Lambda::Version
Properties:
FunctionName: !Ref Func2Function
Description: "Version 1.0.0 of Func2Function"
- Func2FunctionVersion: Defines a version for
Func2Function
.
5.7 Lambda Function 2 Alias
# Alias if Development
Func2FunctionAliasProd:
Type: AWS::Lambda::Alias
Condition: IsDev
Properties:
FunctionName: !Ref Func2Function
FunctionVersion: $LATEST
Name: !Sub "${Stage}"
# Alias if Production
Func2FunctionAliasDev:
Type: AWS::Lambda::Alias
Condition: IsProd
Properties:
FunctionName: !Ref Func2Function
FunctionVersion: !Ref Func2FunctionVersion
Name: !Sub "${Stage}"
- Func2FunctionAliasProd: Creates an alias for
Func2Function
in development, pointing to the$LATEST
version. - Func2FunctionAliasDev: Creates an alias for
Func2Function
in production, pointing to a specific version.
5.8 Lambda Invoke Permissions
# Invoke permissions for Lambda Functions
Func1FunctionInvokePermission:
Type: AWS::Lambda::Permission
Properties:
Action: lambda:InvokeFunction
FunctionName: !Sub "${Func1Function.Arn}:${Stage}"
Principal: apigateway.amazonaws.com
SourceArn: !Sub "arn:aws:execute-api:${AWS::Region}:${AWS::AccountId}:${ApiGatewayApi}/*/*/func1"
Func2FunctionInvokePermission:
Type: AWS::Lambda::Permission
Properties:
Action: lambda:InvokeFunction
FunctionName: !Sub "${Func2Function.Arn}:${Stage}"
Principal: apigateway.amazonaws.com
SourceArn: !Sub "arn:aws:execute-api:${AWS::Region}:${AWS::AccountId}:${ApiGatewayApi}/*/*/func2"
- Func1FunctionInvokePermission: Grants API Gateway permission to invoke
Func1Function
. - Func2FunctionInvokePermission: Grants API Gateway permission to invoke
Func2Function
.
6. Outputs
Outputs:
Func1Api:
Description: "API Gateway endpoint URL for Func1"
Value: !Sub "https://${ApiGatewayApi}.execute-api.${AWS::Region}.amazonaws.com/Prod/func1/"
Func2Api:
Description: "API Gateway endpoint URL for Func2"
Value: !Sub "https://${ApiGatewayApi}.execute-api.${AWS::Region}.amazonaws.com/Prod/func2/"
Func1Function:
Description: "Func1 Lambda Function ARN"
Value: !GetAtt Func1Function.Arn
Func2Function:
Description: "Func2 Lambda Function ARN"
Value: !GetAtt Func2Function.Arn
Func1FunctionIamRole:
Description: "Implicit IAM Role created for Func1"
Value: !GetAtt Func1Function.Arn
Func2FunctionIamRole:
Description: "Implicit IAM Role created for Func2"
Value: !GetAtt Func2Function.Arn
- Outputs: Defines values that can be retrieved after stack creation.
- Func1Api: Provides the endpoint URL for
Func1
through API Gateway. - Func2Api: Provides the endpoint URL for
Func2
through API Gateway. - Func1Function: ARN of the
Func1
Lambda function. - Func2Function: ARN of the
Func2
Lambda function. - Func1FunctionIamRole: ARN of the IAM role implicitly created for
Func1
. - Func2FunctionIamRole: ARN of the IAM role implicitly created for
Func2
.
Deployment
Now that we’ve set up our SAM template and configured our environment, it’s time to deploy our application. Before we deploy, we need to build our application to package the code and dependencies. Let’s walk through the process step-by-step.
1. Build the Project
First, we need to build the SAM application to prepare it for deployment. This step packages your Lambda functions and dependencies.
Run the following command:
sam build
2. Deploy the SAM Template
Once the build is complete, we can deploy the SAM application using the sam deploy --guided
command. This command will prompt you through an interactive setup process, ensuring that all deployment parameters are correctly configured.
sam deploy --guided
You’ll be prompted to provide the following details:
- Stack Name: Enter a name for your CloudFormation stack, such as
func1-and-func2-stack
. - AWS Region: Confirm or enter the AWS region where you want to deploy the stack.
- Confirm Changes Before Deploying: Choose whether you want to review changes before deploying. It’s typically a good practice to review changes before they’re applied.
- Allow SAM CLI to Create Roles with Custom Names: SAM CLI will ask if it can create roles with custom names. Answer
Yes
to permit this - Save Arguments to Configuration File: SAM CLI will offer to save the deployment configuration to a file named
samconfig.toml
. This file stores the parameters you provide, so you don’t need to re-enter them for future deployments. AnswerYes
to save this configuration. - Stage Parameter: When prompted, specify
dev or prod
for the Stage parameter to deploy to the development environment.
3. Verify the Deployment
After deployment, verify the following:
- CloudFormation Stack: Check the CloudFormation console for the status of your stack and its resources.
- API Gateway Endpoints: Test the endpoints for your Lambda functions using the URLs provided by SAM output:
- Lambda Functions: Review the AWS Lambda console for logs and metrics. Use Amazon CloudWatch Logs for troubleshooting.
4. (Optional) Update and Redeploy
For any code or template changes, use the following commands to update your deployment:
sam build
sam deploy
The sam deploy command will use the parameters saved in samconfig.toml, so re-entering them is not necessary.
Conclusion
By following these steps, you’ve successfully built and deployed a SAM-based serverless application featuring multiple Lambda functions and API Gateway configurations. This deployment setup effectively utilizes stage variables to manage different environments, such as development (dev
) and production (prod
).
With your application deployed:
- Multiple Lambda Functions: Your SAM template is configured to deploy multiple Lambda functions (
Func1
andFunc2
), each with its integration endpoint in the API Gateway. This modular approach allows you to manage and scale individual functions independently. - Stage Variables: The use of stage variables (
Stage
) ensures that your application can adapt to different environments with minimal changes. By specifyingdev
orprod
, you configure your Lambda function aliases and API Gateway stages to point to the correct versions of your functions, making it easier to handle deployments across various stages.
Deploying with AWS SAM allows you to efficiently manage multiple serverless functions and environment-specific configurations, simplifying your development and deployment processes. If you encounter any challenges or need additional guidance, the AWS documentation and community forums are valuable resources.
Happy deploying!