How to connect multiple Lambda functions in one API Gateway (SAM Template) with Stage variables
August 5, 2024
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 an IAM policy.
This policy includes actions for creating, updating, and deleting resources, while also enabling S3 operations for deployment artifacts and CloudWatch Logs for function monitoring.
We’ll create a least-privilege IAM policy and role for AWS SAM CLI.
1. Go to IAM Dashboard
- Open the AWS Management Console
- Navigate to IAM
2. Create Policy
- Click Policies from the left menu
- Click Create Policy
- Select the JSON tab
- Paste the following policy
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": "*"
}
]
}
Step 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 permissions 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
- 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
Typescript: Yes
XRay Tracing: No
Enable Cloudwatch Insights: 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
- We then rename our function repo to “func1” and duplicate the repo name “func2”
mv hello-world func1 && cp -r func1 func2
- (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.
- 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).
- 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 is dev.
- IsProd: Checks if the Stage is prod.
- Globals
Globals:
Function:
Timeout: 3
- Globals: Sets default properties for resources.
- Function: Checks if the Stage is dev.
- IsProd: Checks if the Stage is prod.
- 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 aliases Stageusing aws_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.
- 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
Deployment Instructions
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. Answer Yes to save this configuration. - Stage Parameter: When prompted, specify
devorprodfor 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 and Func2), 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 specifying dev or prod, 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!