How to create AWS Lambda with AWS SAM CLI

According to my experience, if you want to create an AWS Lambda without interactive mode, 15 templates below don't exists for each runtime, for example, Data processing template only exists for dotnet and nodejs.

Notice that third template name is Hello World Example with Powertools for AWS Lambda in below output, but if you want to create a project with python runtime and third template in the below sam init output, you have to use hello-world-powertools-python as a template name. But it is not mentioned in help. So, i just used sam init in interactive mode and output of this command showed me template name.

sam init --name my-data-processing-app --runtime python3.9 --dependency-manager pip --app-template hello-world-powertools-python

$ sam init

You can preselect a particular runtime or package type when using the `sam init` experience.
Call `sam init --help` to learn more.

Which template source would you like to use?
	1 - AWS Quick Start Templates
	2 - Custom Template Location
Choice: 1

Choose an AWS Quick Start application template
	1 - Hello World Example
	2 - Data processing
	3 - Hello World Example with Powertools for AWS Lambda
	4 - Multi-step workflow
	5 - Scheduled task
	6 - Standalone function
	7 - Serverless API
	8 - Infrastructure event management
	9 - Lambda Response Streaming
	10 - Serverless Connector Hello World Example
	11 - Multi-step workflow with Connectors
	12 - Full Stack
	13 - Lambda EFS example
	14 - DynamoDB Example
	15 - Machine Learning
Template: 1

Use the most popular runtime and package type? (Python and zip) [y/N]: 
$ sam init --help
Usage: sam init [OPTIONS]

Description:
     
  Initialize a serverless application with an AWS SAM template, source code and 
  structure for serverless abstractions which connect to event source(s) such as APIs,
  S3 Buckets or DynamoDB Tables. This application includes everything one needs to
  get started with serverless and eventually grow into a production scale application.
  
  To explore initializing with your own template and/or using a custom location, 
  please take a look at our official documentation.

  This command may not require access to AWS credentials.
                                    

Examples:

    Interactive Mode:
                                        
        $ sam init                      

    Customized Interactive Mode:
                                        
        $ sam init --name sam-app --runtime nodejs18.x --architecture arm64
                                        
        $ sam init --name sam-app --runtime nodejs18.x --dependency-manager npm --app-template hello-world
                                        
        $ sam init --name sam-app --package-type image --architecture arm64
                                        

    Direct Initialization:
                                        
        $ sam init --location gh:aws-samples/cookiecutter-aws-sam-python
                                        
        $ sam init --location git+ssh://git@github.com/aws-samples/cookiecutter-aws-sam-python.git
                                        
        $ sam init --location /path/to/template.zip
                                        
        $ sam init --location /path/to/template/directory
                                        
        $ sam init --location https://example.com/path/to/template.zip
                                        

Application Options:
                                    
                                    
    -n, --name TEXT                 Name of AWS SAM Application.
                                    
    --architecture ARCHITECTURE     Architectures for Lambda functions.
                                    
                                    Architectures: ['arm64', 'x86_64']
                                    
    --runtime RUNTIME               Lambda runtime for application.
                                    
                                    Runtimes: dotnet6, go1.x, java17, java11, java8.al2, java8, nodejs18.x, nodejs16.x,
                                    nodejs14.x, nodejs12.x, provided, provided.al2, python3.9, python3.8, python3.7,
                                    python3.10, ruby3.2, ruby2.7
                                    
    --dependency-manager DEPENDENCY_MANAGER
                                    Dependency manager for Lambda runtime.
                                    
                                    Dependency managers: bundler, cli-package, pip, maven, npm, gradle, mod
                                    
    -l, --location TEXT             Template location (git, mercurial, http(s), zip, path).
                                    
    --package-type PACKAGE_TYPE     Lambda deployment package type.
                                    
                                    Package Types: Zip, Image
                                    
    --base-image BASE_IMAGE         Lambda base image for deploying IMAGE based package type.
                                    
                                    Base images: amazon/ruby2.7-base, amazon/python3.8-base, amazon/java11-base,
                                    amazon/nodejs14.x-base, amazon/python3.9-base, amazon/nodejs16.x-base,
                                    amazon/nodejs12.x-base, amazon/ruby3.2-base, amazon/python3.7-base,
                                    amazon/java8-base, amazon/go-provided.al2-base, amazon/java8.al2-base,
                                    amazon/python3.10-base, amazon/nodejs18.x-base, amazon/dotnet6-base,
                                    amazon/go1.x-base, amazon/java17-base
                                    
    --app-template TEXT             Identifier of the managed application template to be used. Alternatively, run '$sam
                                    init' without options for an interactive workflow.
                                    
    -o, --output-dir PATH           Directory to initialize AWS SAM application.
                                    

Non Interactive Options:
                                    
                                    
    --no-interactive                Disable interactive prompting for init parameters. (fail if any required values are
                                    missing)
                                    
    --no-input                      Disable Cookiecutter prompting and accept default values defined in the cookiecutter
                                    config.
                                    
    --extra-context TEXT            Override custom parameters in the template's cookiecutter.json configuration e.g.
                                    {"customParam1": "customValue1", "customParam2":"customValue2"}
                                    

Configuration Options:
    Learn more about configuration files at:
                                    
    https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-cli-config.html. 
                                    
                                    
    --config-env TEXT               Environment name specifying default parameter values in the configuration file.
                                    [default: default]
                                    
    --config-file TEXT              Configuration file containing default parameter values.  [default: samconfig.toml]
                                    

Additional Options:
                                    
                                    
    --tracing / --no-tracing        Enable AWS X-Ray tracing for application.
                                    
    --application-insights / --no-application-insights
                                    Enable CloudWatch Application Insights monitoring for application.
                                    

Beta Options:
                                    
                                    
    --beta-features / --no-beta-features
                                    Enable/Disable beta features.
                                    

Other Options:
                                    
                                    
    --debug                         Turn on debug logging to print debug message generated by AWS SAM CLI and display
                                    timestamps.
                                    
    -h, --help                      Show this message and exit.

AWS Runtimes

Find more details in https://docs.aws.amazon.com/lambda/latest/dg/lambda-runtimes.html

NameIdentifierSDKOperating systemArchitecturesDeprecation (Phase 1)
Node.js 20nodejs20.x3.362.0Amazon Linux 2x86_64, arm64
Node.js 18nodejs18.x3.362.0Amazon Linux 2x86_64, arm64
Node.js 16nodejs16.x2.1374.0Amazon Linux 2x86_64, arm64Mar 11, 2024
Node.js 14nodejs14.x2.1374.0Amazon Linux 2x86_64, arm64Nov 27, 2023
Python 3.12python3.12boto3-1.28.83 botocore-1.31.83Amazon Linux 2023x86_64, arm64
Python 3.11python3.11boto3-1.28.83 botocore-1.31.83Amazon Linux 2x86_64, arm64
Python 3.10python3.10boto3-1.26.90 botocore-1.29.90Amazon Linux 2x86_64, arm64
Python 3.9python3.9boto3-1.26.90 botocore-1.29.90Amazon Linux 2x86_64, arm64
Python 3.8python3.8boto3-1.26.90 botocore-1.29.90Amazon Linux 2x86_64, arm64
Python 3.7python3.7boto3-1.26.90 botocore-1.29.90Amazon Linuxx86_64Nov 27, 2023
Java 21java21Amazon Linux 2023x86_64, arm64
Java 17java17Amazon Linux 2x86_64, arm64
Java 11java11Amazon Linux 2x86_64, arm64
Java 8java8.al2Amazon Linux 2x86_64, arm64
Java 8java8Amazon Linuxx86_64Dec 31, 2023
.NET 7 (container only)dotnet7Amazon Linux 2x86_64, arm64May 14, 2024
.NET 6dotnet6Amazon Linux 2x86_64, arm64
Go 1.xgo1.xAmazon Linuxx86_64Dec 31, 2023
Ruby 3.2ruby3.23.1.0Amazon Linux 2x86_64, arm64
Ruby 2.7ruby2.73.1.0Amazon Linux 2x86_64, arm64Dec 7, 2023
Custom Runtimeprovided.al2Amazon Linux 2x86_64, arm64
Custom RuntimeprovidedAmazon Linuxx86_64Dec 31, 2023

Creating Python Lambda

kenanhancer@kenans-MacBook-Pro python % sam init --name sam-app-demo1 --runtime python3.10 --app-template hello-world
sam-app-demo1/
├── README.md
├── __init__.py
├── events
│   └── event.json
├── hello_world
│   ├── __init__.py
│   ├── app.py
│   └── requirements.txt
├── samconfig.toml
├── template.yaml
└── tests
    ├── __init__.py
    ├── integration
    │   ├── __init__.py
    │   └── test_api_gateway.py
    ├── requirements.txt
    └── unit
        ├── __init__.py
        └── test_handler.py

5 directories, 14 files
import json

# import requests


def lambda_handler(event, context):
    """Sample pure Lambda function

    Parameters
    ----------
    event: dict, required
        API Gateway Lambda Proxy Input Format

        Event doc: https://docs.aws.amazon.com/apigateway/latest/developerguide/set-up-lambda-proxy-integrations.html#api-gateway-simple-proxy-for-lambda-input-format

    context: object, required
        Lambda Context runtime methods and attributes

        Context doc: https://docs.aws.amazon.com/lambda/latest/dg/python-context-object.html

    Returns
    ------
    API Gateway Lambda Proxy Output Format: dict

        Return doc: https://docs.aws.amazon.com/apigateway/latest/developerguide/set-up-lambda-proxy-integrations.html
    """

    # try:
    #     ip = requests.get("http://checkip.amazonaws.com/")
    # except requests.RequestException as e:
    #     # Send some context about this error to Lambda Logs
    #     print(e)

    #     raise e

    return {
        "statusCode": 200,
        "body": json.dumps({
            "message": "hello world",
            # "location": ip.text.replace("\n", "")
        }),
    }
kenanhancer@kenans-MacBook-Pro python % cd sam-app-demo1
kenanhancer@kenans-MacBook-Pro sam-app-demo1 % pyenv local 3.10.5
kenanhancer@kenans-MacBook-Pro sam-app-demo1 % python -m venv .venv
kenanhancer@kenans-MacBook-Pro sam-app-demo1 % source .venv/bin/activate

Notice the shell prompt displayed as

kenanhancer@kenans-MacBook-Pro sam-app-demo1 % 

but after activating Python virtual environment, it is changed as;

(.venv) kenanhancer@kenans-MacBook-Pro sam-app-demo1 %

so (.venv) prefix is added in shell prompt.

If your SAM app relies on native dependencies, consider building inside a Docker container to match the AWS Lambda execution environment:

sam build --use-container

The --use-container option tells SAM to build your function inside a Docker container that replicates the AWS Lambda execution environment. The built artifacts are stored in the .aws-sam/build directory for deployment to AWS Lambda

(.venv) kenanhancer@kenans-MacBook-Pro sam-app-demo1 % sam build

Starting Build use cache                                                                                                                          
Manifest file is changed (new hash: c3c57a0bb310fed52e5a83d79fd5e487) or dependency folder (.aws-sam/deps/73ce0ea5-1e56-4d89-9ebb-95f34ea9f14a) is
missing for (HelloWorldFunction), downloading dependencies and copying/building source                                                            
Building codeuri: /Users/kenanhancer/Documents/projects/python/sam-app-demo1/hello_world runtime: python3.10 metadata: {} architecture: x86_64    
functions: HelloWorldFunction                                                                                                                     
 Running PythonPipBuilder:CleanUp                                                                                                                 
 Running PythonPipBuilder:ResolveDependencies                                                                                                     
 Running PythonPipBuilder:CopySource                                                                                                              
 Running PythonPipBuilder:CopySource                                                                                                              

Build Succeeded

Built Artifacts  : .aws-sam/build
Built Template   : .aws-sam/build/template.yaml

Commands you can use next
=========================
[*] Validate SAM template: sam validate
[*] Invoke Function: sam local invoke
[*] Test Function in the Cloud: sam sync --stack-name {{stack-name}} --watch
[*] Deploy: sam deploy --guided
sam-app-demo1/
├── .aws-sam
│   ├── build
│   ├── build.toml
│   ├── cache
│   └── deps
├── .gitignore
├── .python-version
├── .venv
│   ├── bin
│   ├── include
│   ├── lib
│   └── pyvenv.cfg
├── README.md
├── __init__.py
├── events
│   └── event.json
├── hello_world
│   ├── __init__.py
│   ├── app.py
│   └── requirements.txt
├── samconfig.toml
├── template.yaml
└── tests
    ├── __init__.py
    ├── integration
    ├── requirements.txt
    └── unit

13 directories, 14 files
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: >
  sam-app-demo1

  Sample SAM Template for sam-app-demo1

# More info about Globals: https://github.com/awslabs/serverless-application-model/blob/master/docs/globals.rst
Globals:
  Function:
    Timeout: 3

Resources:
  HelloWorldFunction:
    Type: AWS::Serverless::Function # More info about Function Resource: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#awsserverlessfunction
    Properties:
      CodeUri: hello_world/
      Handler: app.lambda_handler
      Runtime: python3.10
      Architectures:
        - x86_64
      Events:
        HelloWorld:
          Type: Api # More info about API Event Source: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#api
          Properties:
            Path: /hello
            Method: get

Outputs:
  # ServerlessRestApi is an implicit API created out of Events key under Serverless::Function
  # Find out more about other implicit resources you can reference within SAM
  # https://github.com/awslabs/serverless-application-model/blob/master/docs/internals/generated_resources.rst#api
  HelloWorldApi:
    Description: "API Gateway endpoint URL for Prod stage for Hello World function"
    Value: !Sub "https://${ServerlessRestApi}.execute-api.${AWS::Region}.amazonaws.com/Prod/hello/"
  HelloWorldFunction:
    Description: "Hello World Lambda Function ARN"
    Value: !GetAtt HelloWorldFunction.Arn
  HelloWorldFunctionIamRole:
    Description: "Implicit IAM Role created for Hello World function"
    Value: !GetAtt HelloWorldFunctionRole.Arn
  HelloWorldFunctionName:
    Description: "Hello World Lambda Function Name"
    Value: !Ref HelloWorldFunction
# More information about the configuration file can be found here:
# https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-cli-config.html
version = 0.1

[default]
[default.global.parameters]
stack_name = "sam-app-demo1"

[default.build.parameters]
cached = true
parallel = true

[default.validate.parameters]
lint = true

[default.deploy.parameters]
capabilities = "CAPABILITY_IAM"
confirm_changeset = true
resolve_s3 = true
s3_prefix = "sam-app-demo1"
region = "eu-west-2"
image_repositories = []

[default.package.parameters]
resolve_s3 = true

[default.sync.parameters]
watch = true

[default.local_start_api.parameters]
warm_containers = "EAGER"

[default.local_start_lambda.parameters]
warm_containers = "EAGER"

Deploying

$ sam build
$ sam deploy --guided
$ sam deploy

Print CloudFormation Output variables

Find stack_name in samconfig.toml config file and use in --stack-name parameter

In AWS CloudFormation and the AWS Serverless Application Model (SAM), the name you give to a resource (like "HelloWorldFunction" in your template) is a logical ID. This logical ID is unique within the CloudFormation stack and is used to refer to the resource within the template. However, when the stack is deployed, AWS often appends a unique identifier to the names of the resources to ensure they are globally unique and to avoid naming conflicts during subsequent deployments.

For the AWS Lambda function created by SAM, the deployed function name will usually have the format:

<StackName>-<LogicalFunctionName>-<RandomCharacters>

Notice below output of command, HelloWorldFunctionName value is sam-app-demo1-HelloWorldFunction-30wAqhKAlIN1 , sam deploy generates changed function name in terms of above info.

$ aws cloudformation describe-stacks --stack-name sam-app-demo1 --query "Stacks[0].Outputs"

[
    {
        "OutputKey": "HelloWorldFunctionIamRole",
        "OutputValue": "arn:aws:iam::111111111111:role/sam-app-demo1-HelloWorldFunctionRole-1EUQN2XJ2UAOR",
        "Description": "Implicit IAM Role created for Hello World function"
    },
    {
        "OutputKey": "HelloWorldApi",
        "OutputValue": "https://hg6ru01vu4.execute-api.eu-west-2.amazonaws.com/Prod/hello/",
        "Description": "API Gateway endpoint URL for Prod stage for Hello World function"
    },
    {
        "OutputKey": "HelloWorldFunctionName",
        "OutputValue": "sam-app-demo1-HelloWorldFunction-30wAqhKAlIN1",
        "Description": "Hello World Lambda Function Name"
    },
    {
        "OutputKey": "HelloWorldFunction",
        "OutputValue": "arn:aws:lambda:eu-west-2:111111111111:function:sam-app-demo1-HelloWorldFunction-30wAqhKAlIN1",
        "Description": "Hello World Lambda Function ARN"
    }
]

Test your function with AWS SAM CLI

$ sam local invoke "HelloWorldFunction"

Invoking app.lambda_handler (python3.10)                                                                 
Local image is up-to-date                                                                                
Using local image: public.ecr.aws/lambda/python:3.10-rapid-x86_64.                                       
                                                                                                         
Mounting /Users/kenanhancer/Documents/projects/python/sam-app-demo1/.aws-sam/build/HelloWorldFunction as 
/var/task:ro,delegated, inside runtime container                                                         
START RequestId: e8e32b98-55df-4d7a-bab9-a053f6a1aed9 Version: $LATEST
END RequestId: e8e32b98-55df-4d7a-bab9-a053f6a1aed9
REPORT RequestId: e8e32b98-55df-4d7a-bab9-a053f6a1aed9  Init Duration: 0.52 ms  Duration: 210.84 ms     Billed Duration: 211 ms  Memory Size: 128 MB     Max Memory Used: 128 MB
{"statusCode": 200, "body": "{\"message\": \"hello world\"}"}% 
$ sam local invoke "HelloWorldFunction" -e events/event.json

Invoking app.lambda_handler (python3.10)                                                                 
Local image is up-to-date                                                                                
Using local image: public.ecr.aws/lambda/python:3.10-rapid-x86_64.                                       
                                                                                                         
Mounting /Users/kenanhancer/Documents/projects/python/sam-app-demo1/.aws-sam/build/HelloWorldFunction as 
/var/task:ro,delegated, inside runtime container                                                         
START RequestId: 787c1967-c851-4c60-995b-bc74770c3b0f Version: $LATEST
END RequestId: 787c1967-c851-4c60-995b-bc74770c3b0f
REPORT RequestId: 787c1967-c851-4c60-995b-bc74770c3b0f  Init Duration: 0.58 ms  Duration: 210.20 ms     Billed Duration: 211 ms  Memory Size: 128 MB     Max Memory Used: 128 MB
{"statusCode": 200, "body": "{\"message\": \"hello world\"}"}%  
$ echo '{"message": "hello!"}' | sam local invoke HelloWorldFunction -e -

Reading invoke payload from stdin (you can also pass it from file with --event)                          
Invoking app.lambda_handler (python3.10)                                                                 
Local image is up-to-date                                                                                
Using local image: public.ecr.aws/lambda/python:3.10-rapid-x86_64.                                       
                                                                                                         
Mounting /Users/kenanhancer/Documents/projects/python/sam-app-demo1/.aws-sam/build/HelloWorldFunction as 
/var/task:ro,delegated, inside runtime container                                                         
START RequestId: cdc59941-7437-4da3-9597-8d56a1820168 Version: $LATEST
END RequestId: cdc59941-7437-4da3-9597-8d56a1820168
REPORT RequestId: cdc59941-7437-4da3-9597-8d56a1820168  Init Duration: 0.48 ms  Duration: 232.18 ms     Billed Duration: 233 ms  Memory Size: 128 MB     Max Memory Used: 128 MB
{"statusCode": 200, "body": "{\"message\": \"hello world\"}"}%  

Test your function with AWS CLI

Use function name as sam-app-demo1-HelloWorldFunction-30wAqhKAlIN1 instead of HelloWorldFunction in the below command

$ aws lambda invoke \
  --function-name sam-app-demo1-HelloWorldFunction-30wAqhKAlIN1 \
  --region eu-west-2 \
  --payload $(echo '{"name": "kenan"}' | base64) \
  /dev/stdout

{"statusCode": 200, "body": "{\"message\": \"hello world\"}"}
    "StatusCode": 200,
    "ExecutedVersion": "$LATEST"
}

Test your function with curl

$ curl --request POST https://hg6ru01vu4.execute-api.eu-west-2.amazonaws.com/Prod/hello/ --data '{"name": "kenan"}'

{"message": "hello world"}

Why do i need to create a Python virtual environment for AWS SAM Python App?

I really chat with ChatGPT on it, and realized that virtual environment is not needed if you created your AWS Python Lambda project with AWS SAM CLI, because running sam build will already isolate packages in project directory and somehow will not use global pip or virtual environment `pip`
So I had a very long conversation. This is the thing, ChatGPT recommended in final round 🙂

Adding new endpoint

AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: >
  sam-app-demo1

  Sample SAM Template for sam-app-demo1

# More info about Globals: https://github.com/awslabs/serverless-application-model/blob/master/docs/globals.rst
Globals:
  Function:
    Timeout: 3

Resources:
  HelloWorldFunction:
    Type: AWS::Serverless::Function # More info about Function Resource: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#awsserverlessfunction
    Properties:
      CodeUri: hello_world/
      Handler: app.lambda_handler
      Runtime: python3.10
      Architectures:
        - x86_64
      Events:
        HelloWorldPost:
          Type: Api # More info about API Event Source: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#api
          Properties:
            Path: /hello
            Method: post
        HelloWorldGet:
          Type: Api # More info about API Event Source: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#api
          Properties:
            Path: /hello
            Method: get

Outputs:
  # ServerlessRestApi is an implicit API created out of Events key under Serverless::Function
  # Find out more about other implicit resources you can reference within SAM
  # https://github.com/awslabs/serverless-application-model/blob/master/docs/internals/generated_resources.rst#api
  HelloWorldApi:
    Description: "API Gateway endpoint URL for Prod stage for Hello World function"
    Value: !Sub "https://${ServerlessRestApi}.execute-api.${AWS::Region}.amazonaws.com/Prod/hello/"
  HelloWorldFunction:
    Description: "Hello World Lambda Function ARN"
    Value: !GetAtt HelloWorldFunction.Arn
  HelloWorldFunctionIamRole:
    Description: "Implicit IAM Role created for Hello World function"
    Value: !GetAtt HelloWorldFunctionRole.Arn
  HelloWorldFunctionName:
    Description: "Hello World Lambda Function Name"
    Value: !Ref HelloWorldFunction
$ sam build
$ sam deploy