How to connect multiple Lambda functions in one API Gateway (SAM Template) with Stage variables

Joshua Ochia
9 min readAug 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:

  1. AWS CLI: Installed and configured on your local machine.
  2. AWS SAM CLI: Installed and configured on your local machine.
  3. AWS Account: An active AWS account with permissions to create and manage Lambda functions and API Gateway resources.
  4. IAM Roles: Basic understanding of AWS IAM roles and permissions required for Lambda and API Gateway.
  5. 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.

  1. 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

  1. 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.

  1. 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

  1. 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 is dev.
  • IsProd: Checks if the Stage is prod.

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 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.

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:

  1. Stack Name: Enter a name for your CloudFormation stack, such as func1-and-func2-stack.
  2. AWS Region: Confirm or enter the AWS region where you want to deploy the stack.
  3. 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.
  4. 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
  5. 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.
  6. Stage Parameter: When prompted, specify dev or prodfor 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!

--

--

Joshua Ochia
Joshua Ochia

Written by Joshua Ochia

Cloud Engineer specializing in AWS. Building scalable and efficient Cloud Infrastructure with experience in deploying and managing AWS resources

No responses yet