diff --git a/aws-node-typescript-multi-instance-lambda/.eslintrc.js b/aws-node-typescript-multi-instance-lambda/.eslintrc.js
new file mode 100644
index 000000000..36123a34d
--- /dev/null
+++ b/aws-node-typescript-multi-instance-lambda/.eslintrc.js
@@ -0,0 +1,18 @@
+module.exports = {
+ parser: "@typescript-eslint/parser",
+ parserOptions: {
+ project: "./tsconfig.json",
+ ecmaVersion: 2018,
+ sourceType: "module",
+ },
+ env: {
+ "jest/globals": true,
+ es6: true,
+ },
+ plugins: [
+ "jest",
+ "sonarjs",
+ "@typescript-eslint",
+ "prettier",
+ ],
+};
diff --git a/aws-node-typescript-multi-instance-lambda/.gitignore b/aws-node-typescript-multi-instance-lambda/.gitignore
new file mode 100644
index 000000000..02ffabe9e
--- /dev/null
+++ b/aws-node-typescript-multi-instance-lambda/.gitignore
@@ -0,0 +1,105 @@
+# Logs
+logs
+*.log
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+lerna-debug.log*
+
+# Diagnostic reports (https://nodejs.org/api/report.html)
+report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
+
+# Runtime data
+pids
+*.pid
+*.seed
+*.pid.lock
+
+# Directory for instrumented libs generated by jscoverage/JSCover
+lib-cov
+
+# Coverage directory used by tools like istanbul
+coverage
+*.lcov
+
+# nyc test coverage
+.nyc_output
+
+# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
+.grunt
+
+# Bower dependency directory (https://bower.io/)
+bower_components
+
+# node-waf configuration
+.lock-wscript
+
+# Compiled binary addons (https://nodejs.org/api/addons.html)
+build/Release
+
+# Dependency directories
+node_modules/
+jspm_packages/
+
+# TypeScript v1 declaration files
+typings/
+
+# TypeScript cache
+*.tsbuildinfo
+
+# Optional npm cache directory
+.npm
+
+# Optional eslint cache
+.eslintcache
+
+# Microbundle cache
+.rpt2_cache/
+.rts2_cache_cjs/
+.rts2_cache_es/
+.rts2_cache_umd/
+
+# Optional REPL history
+.node_repl_history
+
+# Output of 'npm pack'
+*.tgz
+
+# Yarn Integrity file
+.yarn-integrity
+
+# dotenv environment variables file
+.env
+.env.test
+
+# parcel-bundler cache (https://parceljs.org/)
+.cache
+
+# Next.js build output
+.next
+
+# Nuxt.js build / generate output
+.nuxt
+dist
+
+# Gatsby files
+.cache/
+# Comment in the public line in if your project uses Gatsby and *not* Next.js
+# https://nextjs.org/blog/next-9-1#public-directory-support
+# public
+
+# vuepress build output
+.vuepress/dist
+
+# Serverless directories
+.serverless/
+.webpack/
+
+# FuseBox cache
+.fusebox/
+
+# DynamoDB Local files
+.dynamodb/
+
+# TernJS port file
+.tern-port
diff --git a/aws-node-typescript-multi-instance-lambda/.images/aws-lambda-screenshot.png b/aws-node-typescript-multi-instance-lambda/.images/aws-lambda-screenshot.png
new file mode 100644
index 000000000..a90b471ff
Binary files /dev/null and b/aws-node-typescript-multi-instance-lambda/.images/aws-lambda-screenshot.png differ
diff --git a/aws-node-typescript-multi-instance-lambda/.nvmrc b/aws-node-typescript-multi-instance-lambda/.nvmrc
new file mode 100644
index 000000000..25bf17fc5
--- /dev/null
+++ b/aws-node-typescript-multi-instance-lambda/.nvmrc
@@ -0,0 +1 @@
+18
\ No newline at end of file
diff --git a/aws-node-typescript-multi-instance-lambda/LICENSE b/aws-node-typescript-multi-instance-lambda/LICENSE
new file mode 100644
index 000000000..026c439aa
--- /dev/null
+++ b/aws-node-typescript-multi-instance-lambda/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2022 Daniel Simpson
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/aws-node-typescript-multi-instance-lambda/README.md b/aws-node-typescript-multi-instance-lambda/README.md
new file mode 100644
index 000000000..dbd522d5c
--- /dev/null
+++ b/aws-node-typescript-multi-instance-lambda/README.md
@@ -0,0 +1,78 @@
+# Multi-instance Serverless AWS Lambda function example
+
+This example demonstrates how to deploy multiple instances (or copies) of the same lambda function by using the "--param" `sls` option to pass in an instance name. The main idea is to rename AWS resources and outputs which would otherwise clash using the instance name.
+
+A lambda instance's configuration can then be made instance-specific by adding the instance name to its environment variable or parameter store keys.
+
+The benefit of this approach is that only one lambda function needs to be defined in `serverless.ts` (or `serverless.yml`), but any number of copies of it can be deployed directly from the command line.
+
+## Setup
+
+1. Install Node.js 18 (recommended: [Node Version Manager](https://github.com/nvm-sh/nvm#install--update-script)).
+
+2. Install packages:
+ ```bash
+ npm i
+ ```
+
+## Use case
+
+When you want to deploy multiple lambda functions with the same implementation, but you don't want to explicitly define each copy in `serverless.yml`.
+
+For example, you could deploy the lambda copies in a continuous deployment (CD) pipeline and configure them to fetch data from different sources based on their instance names.
+
+## Usage
+
+### Deployment
+
+To deploy an instance "foo" of the lambda `hello` to the default `dev` stage in the `ap-southeast-2` region , run:
+
+```bash
+npx sls deploy --region ap-southeast-2 --param="instance=foo"
+```
+
+Another instance, "bar", can be deployed with:
+
+```bash
+npx sls deploy --region ap-southeast-2 --param="instance=bar"
+```
+
+Now, two separate yet identical `hello` lambdas are available on AWS:
+
+
+
+Note that the "--param" option is now required when running other `serverless` commands, such as `sls info`:
+
+```bash
+npx sls info --region ap-southeast-2 --param="instance=foo"
+```
+
+### Invocation
+
+To invoke an instance of `hello` using `sls invoke`, pass the instance name to the "--param" option:
+
+```bash
+npx sls invoke -f hello --region ap-southeast-2 --param="instance=foo"
+```
+
+Output:
+
+```
+{
+ "statusCode": 200,
+ "body": "{\n \"message\": \"Function `aws-node-typescript-multi-instance-lambda-foo-dev-hello` executed successfully.\",\n \"input\": {}\n}"
+}
+```
+
+### Removal
+
+"--param" is also required to run the `sls remove` command to tear down the CloudFormation stack associated with a specific instance.
+
+```bash
+npx sls remove --region ap-southeast-2 --param="instance=foo"
+```
+
+## Acknowledgements
+
+- [This comment](https://github.com/serverless/serverless/issues/9361#issuecomment-884602588) by [**@rdemorais**](https://github.com/rdemorais) on Serverless issue [#9361](https://github.com/serverless/serverless/issues/9361).
+- [**@billkidwell**](https://github.com/billkidwell)'s [Simple Kinesis Example](https://github.com/serverless/examples/tree/v3/aws-node-typescript-kinesis).
diff --git a/aws-node-typescript-multi-instance-lambda/handler.ts b/aws-node-typescript-multi-instance-lambda/handler.ts
new file mode 100644
index 000000000..6f902e515
--- /dev/null
+++ b/aws-node-typescript-multi-instance-lambda/handler.ts
@@ -0,0 +1,13 @@
+import { Context } from "aws-lambda";
+
+module.exports.hello = async (event: unknown, context: Context) => ({
+ statusCode: 200,
+ body: JSON.stringify(
+ {
+ message: `Function \`${context.functionName}\` executed successfully.`,
+ input: event,
+ },
+ null,
+ 2
+ ),
+});
diff --git a/aws-node-typescript-multi-instance-lambda/package.json b/aws-node-typescript-multi-instance-lambda/package.json
new file mode 100644
index 000000000..1591f1a07
--- /dev/null
+++ b/aws-node-typescript-multi-instance-lambda/package.json
@@ -0,0 +1,33 @@
+{
+ "name": "aws-node-typescript-multi-instance-lambda",
+ "version": "1.0.0",
+ "description": "Serverless example demonstrating how to deploy multiple instances of the same lambda",
+ "main": "serverless.ts",
+ "scripts": {
+ "test": "echo \"Error: no test specified\" && exit 1"
+ },
+ "author": "Daniel Simpson",
+ "license": "MIT",
+ "devDependencies": {
+ "@serverless/typescript": "^3.25.0",
+ "@types/aws-lambda": "^8.10.109",
+ "@types/node": "^18.11.11",
+ "@typescript-eslint/eslint-plugin": "^5.45.1",
+ "@typescript-eslint/parser": "^5.45.1",
+ "eslint": "^8.29.0",
+ "import": "^0.0.6",
+ "jest": "^29.3.1",
+ "prettier": "^2.8.1",
+ "serverless": "^3.25.1",
+ "serverless-webpack": "^5.11.0",
+ "sonarjs": "^1.0.0",
+ "ts-loader": "^9.4.2",
+ "ts-node": "^10.9.1",
+ "typescript": "^4.9.3",
+ "webpack": "^5.75.0"
+ },
+ "dependencies": {
+ "aws-lambda": "^1.0.7",
+ "source-map-support": "^0.5.21"
+ }
+}
diff --git a/aws-node-typescript-multi-instance-lambda/serverless.ts b/aws-node-typescript-multi-instance-lambda/serverless.ts
new file mode 100644
index 000000000..d8b3a46f9
--- /dev/null
+++ b/aws-node-typescript-multi-instance-lambda/serverless.ts
@@ -0,0 +1,105 @@
+import type { AWS } from "@serverless/typescript";
+
+const serverlessConfiguration: AWS = {
+ service: "aws-node-typescript-multi-instance-lambda",
+ frameworkVersion: "3",
+ configValidationMode: "error",
+ plugins: ["serverless-webpack"],
+ provider: {
+ name: "aws",
+ runtime: "nodejs18.x",
+ // region: "ap-southeast-2",
+ // Rename CloudFormation stack to include the instance name
+ stackName: "${self:service}-${param:instance}-${sls:stage}",
+ },
+ functions: {
+ hello: {
+ // Rename function to include the instance name
+ name: "${self:service}-${param:instance}-${sls:stage}-hello",
+ handler: "handler.hello",
+ environment: {
+ INSTANCE_NAME: "${param:instance}",
+ },
+ },
+ },
+
+ resources: {
+ Resources: {
+ IamRoleLambdaExecution: {
+ Type: "AWS::IAM::Role",
+ Properties: {
+ Policies: [
+ {
+ PolicyName: {
+ "Fn::Join": [
+ "-",
+ [
+ "${self:service}",
+ "${param:instance}",
+ "${sls:stage}",
+ "lambda",
+ ],
+ ],
+ },
+ },
+ ],
+ // Include instance name to avoid resource collision between instances
+ RoleName: {
+ "Fn::Join": [
+ "-",
+ [
+ // "${self:service}",
+ "multi-instance", // shorten service name due to 64-character "roleName" length requirement
+ "${param:instance}",
+ "${sls:stage}",
+ {
+ Ref: "AWS::Region",
+ },
+ "lambdaRole",
+ ],
+ ],
+ },
+ },
+ },
+ },
+ Outputs: {
+ // Rename output export names to include instance name
+ // https://github.com/serverless/serverless/issues/9361#issuecomment-884602588
+ // https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/outputs-section-structure.html
+ HelloLambdaFunctionQualifiedArn: {
+ Export: {
+ Name: {
+ "Fn::Join": [
+ "-",
+ [
+ "sls",
+ "${self:service}",
+ "${param:instance}",
+ "${sls:stage}",
+ "HelloLambdaFunctionQualifiedArn",
+ ],
+ ],
+ },
+ },
+ },
+ ServerlessDeploymentBucketName: {
+ Export: {
+ Name: {
+ "Fn::Join": [
+ "-",
+ [
+ "sls",
+ "${self:service}",
+ "${param:instance}",
+ "${sls:stage}",
+ "ServerlessDeploymentBucketName",
+ ],
+ ],
+ },
+ },
+ },
+ },
+ },
+};
+
+module.exports = serverlessConfiguration;
diff --git a/aws-node-typescript-multi-instance-lambda/tsconfig.json b/aws-node-typescript-multi-instance-lambda/tsconfig.json
new file mode 100644
index 000000000..c2db12811
--- /dev/null
+++ b/aws-node-typescript-multi-instance-lambda/tsconfig.json
@@ -0,0 +1,21 @@
+{
+ "compilerOptions": {
+ "module": "commonjs",
+ "moduleResolution": "node",
+ "outDir": "lib",
+ "strict": true,
+ "noUnusedLocals": true,
+ "noUnusedParameters": true,
+ "noImplicitReturns": true,
+ "noFallthroughCasesInSwitch": true,
+ "noUncheckedIndexedAccess": true,
+ "noImplicitOverride": true,
+ "noPropertyAccessFromIndexSignature": true,
+ "noErrorTruncation": true,
+ "skipLibCheck": true,
+ "forceConsistentCasingInFileNames": true,
+ "esModuleInterop": true
+ },
+ "include": ["handler.ts", "serverless.ts", "webpack.config.js"],
+ "exclude": ["node_modules/**/*", ".serverless/**/*"]
+}
diff --git a/aws-node-typescript-multi-instance-lambda/webpack.config.js b/aws-node-typescript-multi-instance-lambda/webpack.config.js
new file mode 100644
index 000000000..4fba40cd0
--- /dev/null
+++ b/aws-node-typescript-multi-instance-lambda/webpack.config.js
@@ -0,0 +1,23 @@
+const path = require('path');
+const slsw = require('serverless-webpack');
+
+module.exports = {
+ mode: slsw.lib.webpack.isLocal ? 'development' : 'production',
+ entry: slsw.lib.entries,
+ devtool: 'source-map',
+ resolve: {
+ extensions: ['.js', '.jsx', '.json', '.ts', '.tsx'],
+ },
+ output: {
+ libraryTarget: 'commonjs',
+ path: path.join(__dirname, '.webpack'),
+ filename: '[name].js',
+ },
+ target: 'node',
+ module: {
+ rules: [
+ // all files with a `.ts` or `.tsx` extension will be handled by `ts-loader`
+ { test: /\.tsx?$/, loader: 'ts-loader' },
+ ],
+ },
+};
\ No newline at end of file