Developing AWS Lambda Functions In Locally
Tags: aws, tutorial, lambda
Published: 2024-01-06 14:22:09

## 1. Introduction

In this article, we are going to learn about Lambda function creating, building, updating, and testing locally. 

We can use **_AWS SAM_** to achieve this.

**1.1 What is SAM**

the AWS Serverless Application Model (SAM) is an open-source framework for building serverless applications. It provides shorthand syntax to express functions, APIs, databases, and event source mappings. With just a few lines per resource, you can define the application you want and model it using YAML.

During deployment, SAM transforms and expands the SAM syntax into AWS CloudFormation syntax, enabling you to build serverless applications faster. 

## 2. Setup the environment

**2.1 Install AWS SAM CLI**

You need to follow the below steps to install the [AWS SAM](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/install-sam-cli.html).

**2.2 Install Docker Desktop**

Follow, and [install the docker application](https://www.docker.com/products/docker-desktop/) on your desktop.

**2.3 Authenticate AWS using AWS CLI**

Follow [AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html), to authenticate your AWS account. 

## 3. Create a project

**Step 01:** Create a folder and go inside of that folder. Here I'm using "sam-projects". 

Inside of the folder run the below command to make sure your SAM installation is successful. 

```
sam -h
```

**Step 02:** To create a new project run the `sam init` command.

I'm using AWS Quick start template. 

```
PS C:\sam_projects\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 - GraphQLApi Hello World Example
        13 - Full Stack
        14 - Lambda EFS example
        15 - Hello World Example With Powertools for AWS Lambda
        16 - DynamoDB Example
        17 - Machine Learning
Template: 1

Use the most popular runtime and package type? (Python and zip) [y/N]: y

Would you like to enable X-Ray tracing on the function(s) in your application?  [y/N]: N

Would you like to enable monitoring using CloudWatch Application Insights?
For more info, please view https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/cloudwatch-application-insights.html [y/N]: N

Would you like to set Structured Logging in JSON format on your Lambda functions?  [y/N]: y
Structured Logging in JSON format might incur an additional cost. View https://docs.aws.amazon.com/lambda/latest/dg/monitoring-cloudwatchlogs.html#monitoring-cloudwatchlogs-pricing for more details

Project name [sam-app]: sam-app2

Cloning from https://github.com/aws/aws-sam-cli-app-templates (process may take a moment)

-----------------------
    Generating application:
    -----------------------
    Name: sam-app2
    Runtime: python3.9
    Architectures: x86_64
    Dependency Manager: pip
    Application Template: hello-world
    Output Directory: .
    Configuration file: sam-app2\samconfig.toml

    Next steps can be found in the README file at sam-app2\README.md


Commands you can use next
=========================
[*] Create pipeline: cd sam-app2 && sam pipeline init --bootstrap
[*] Validate SAM template: cd sam-app2 && sam validate
[*] Test Function in the Cloud: cd sam-app2 && sam sync --stack-name {stack-name} --watch
```

Now you see your project has been created. 


![sam-app2](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/h083ioxnsjn4s6p69na3.png)

**Step 03:** Explaining project structure 

** I have deleted "tests" folder. We do not need that for this lesson. 

Let's start with `template.yaml` file. 

SAM is that it deploys and maintains all of your resources, this is very similar to CloudFormation, actually this uses CloudFormation to deploy your resources and create a change set to update your resources. 

`template.yaml`

```
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: >
  sam-app2

  Sample SAM Template for sam-app2

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

    # You can add LoggingConfig parameters such as the Logformat, Log Group, and SystemLogLevel or ApplicationLogLevel. Learn more here https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-resource-function.html#sam-function-loggingconfig.
    LoggingConfig:
      LogFormat: JSON
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.9
      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

```

So here, we **do not need** the below sections. I'm removing them to keep simple of the template.yaml file.  

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

and,

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

and also,

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

    # You can add LoggingConfig parameters such as the Logformat, Log Group, and SystemLogLevel or ApplicationLogLevel. Learn more here https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-resource-function.html#sam-function-loggingconfig.
    LoggingConfig:
      LogFormat: JSON
```

My python runtime environment is set to Python 3.12, hence I'm changing it. 

Final `template.yaml`

```
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: >
  sam-app2

  Sample SAM Template for sam-app2

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.12
      Architectures:
      - x86_64

```

The resource name is "HelloWorldFunction"

So we have our actual lambda function called "app.py"

![app.py](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ct06bxgv6r0w2vhjo1tx.png)

`app.py`

```
import json

def lambda_handler(event, context):
 
    return {
        "statusCode": 200,
        "body": json.dumps({
            "message": "hello world",
            # "location": ip.text.replace("\n", "")
        }),
    }
```

Events: We can send test events to our function to test it locally.

**Step 04:** Now we want to rebuild this so, we are using `sam build`. This creates like a build folder of our latest lambda function. 

```
PS C:\sam_projects\sam-app2> sam build
Starting Build use cache
Manifest file is changed (new hash: 3298f13ca931dd4d421) or dependency folder (.aws-sam\deps\48421c5a-c936-9d9-443b05eeae8c) is missing for (HelloWorldFunction), downloading dependencies and
copying/building source
Building codeuri: C:\sam_projects\sam-app2\hello_world runtime: python3.12 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
```

So now we can see `.aws-sam` folder below, 

![aws-sam-folder](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kd50eunq35b5em7r7v1c.png)

In the build folder within that in our lambda function this hello world function and see it has all of the requests dependencies so we could just right-click open it and zip it up and upload it in lambda it's it's everything you need all of your dependencies in one folder. 

![buiildfolder](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ailh49souxn6y1n1cj2y.png)

Every time you make changes make sure to rebuild. 

**Step 05:**

let's say this is the first iteration and we want to deploy these resources it's as simple as and you see it right here in the commands to deploy sam deploy guided guided kind of walks us through the first process of deploying it so let's type that in
`sam deploy --guided`

```
PS C:\sam_projects\sam-app2> sam deploy --guided

Configuring SAM deploy
======================

        Looking for config file [samconfig.toml] :  Found
        Reading default arguments  :  Success

        Setting default arguments for 'sam deploy'
        =========================================
        Stack Name [sam-app2]:
        AWS Region [us-east-1]: us-west-2
        #Shows you resources changes to be deployed and require a 'Y' to initiate deploy
        Confirm changes before deploy [Y/n]: Y
        #SAM needs permission to be able to create roles to connect to the resources in your template
        Allow SAM CLI IAM role creation [Y/n]: Y
        #Preserves the state of previously provisioned resources when an operation fails
        Disable rollback [y/N]: y
        Save arguments to configuration file [Y/n]: Y
        SAM configuration file [samconfig.toml]: 
        SAM configuration environment [default]: 

        Looking for resources needed for deployment:
        Creating the required resources...
        Successfully created!

        Managed S3 bucket: aws-sam-cli-managed-default-samclisourcebucket-xxxxxxxx
        A different default S3 bucket can be set in samconfig.toml and auto resolution of buckets turned off by setting resolve_s3=False

        Parameter "stack_name=sam-app2" in [default.deploy.parameters] is defined as a global parameter [default.global.parameters].
        This parameter will be only saved under [default.global.parameters] in C:\sam_projects\sam-app2\samconfig.toml.

        Saved arguments to config file
        Running 'sam deploy' for future deployments will use the parameters saved above.
        The above parameters can be changed by modifying samconfig.toml
        Learn more about samconfig.toml syntax at
        https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-cli-config.html

        Uploading to sam-app2/c43cafb817866506  532993 / 532993  (100.00%)

        Deploying with following values
        ===============================
        Stack name                   : sam-app2
        Region                       : us-west-2
        Confirm changeset            : True
        Disable rollback             : True
        Deployment s3 bucket         : aws-sam-cli-managed-default-samclisourcebucket-xxxxxxx
        Capabilities                 : ["CAPABILITY_IAM"]
        Parameter overrides          : {}
        Signing Profiles             : {}

Initiating deployment
=====================

        Uploading to sam-app2/86dc15e504ed4983627c.template  516 / 516  (100.00%)


Waiting for changeset to be created..

CloudFormation stack changeset
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------    
Operation                                               LogicalResourceId                                       ResourceType                                            Replacement                                              
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------    
+ Add                                                   HelloWorldFunctionRole                                  AWS::IAM::Role                                          N/A
+ Add                                                   HelloWorldFunction                                      AWS::Lambda::Function                                   N/A                                                      
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------    


Changeset created successfully. arn:aws:cloudformation:us-west-2:xxxxxxxx:changeSet/samcli-deploy17981/5866bc24-2


Previewing CloudFormation changeset before deployment
======================================================
Deploy this changeset? [y/N]:
```

Now if we go to cloud formation we should see two stacks if this is the first time you've deployed with SAM you'll see two stacks.

The first stack is the SAM CLI managed default and this is it creates an s3 bucket to store your artifacts so this is a one-time thing once you do this the first time you use sam then the subsequent deployments your artifacts will just be added to this. 

The other stack named sam-app2 is your resource this is what we're deploying so it's a status of review in progress let's go back to our project and see that it's waiting for me to confirm it. 


![stack](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/un0xqa6mrczib98ybb3b.png)

```
Waiting for changeset to be created..

CloudFormation stack changeset
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------    
Operation                                               LogicalResourceId                                       ResourceType                                            Replacement                                              
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------    
+ Add                                                   HelloWorldFunctionRole                                  AWS::IAM::Role                                          N/A
+ Add                                                   HelloWorldFunction                                      AWS::Lambda::Function                                   N/A                                                      
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------    


Changeset created successfully. arn:aws:cloudformation:us-west-2:xxxxxxxx:changeSet/samcli-deploy11/5866b57-08875


Previewing CloudFormation changeset before deployment
======================================================
Deploy this changeset? [y/N]: Y
```

choose `Y` and press enter. It should deploy the lambda function in the role. 

Now you can see the below in your CMD. 

```
Deploy this changeset? [y/N]: Y

2024-01-06 17:18:59 - Waiting for stack create/update to complete

CloudFormation events from stack operations (refresh every 5.0 seconds)
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------    
ResourceStatus                                          ResourceType                                            LogicalResourceId                                       ResourceStatusReason                                     
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------    
CREATE_IN_PROGRESS                                      AWS::CloudFormation::Stack                              sam-app2                                                User Initiated
CREATE_IN_PROGRESS                                      AWS::IAM::Role                                          HelloWorldFunctionRole                                  -                                                        
CREATE_IN_PROGRESS                                      AWS::IAM::Role                                          HelloWorldFunctionRole                                  Resource creation Initiated                              
CREATE_COMPLETE                                         AWS::IAM::Role                                          HelloWorldFunctionRole                                  -
CREATE_IN_PROGRESS                                      AWS::Lambda::Function                                   HelloWorldFunction                                      -                                                        
CREATE_IN_PROGRESS                                      AWS::Lambda::Function                                   HelloWorldFunction                                      Resource creation Initiated
CREATE_COMPLETE                                         AWS::Lambda::Function                                   HelloWorldFunction                                      -
CREATE_COMPLETE                                         AWS::CloudFormation::Stack                              sam-app2                                                -                                                        
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------    


Successfully created/updated stack - sam-app2 in us-west-2
```

Now you can see in the CloudFormation that "sam-app2" is completed. 

![samapp2complete](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3ow4aznwl71hjexmunc5.png)

If we go to lambda functions we should see that function. 


![lambda](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9xp4h78ydhd3k58udjwb.png)

So we learned how to create one how to build it how to, deploy it Now we need to learn how to test it and how to update it. 

**Step 06:** Test it and Update it. 

So go back to your lambda function be sure you're not working in the build folder go back to your lambda function your `app.py` and let's add some stuff to it. 

`app.py`

```
import json

def lambda_handler(event, context):

    first_name = event['first_name']
    message = event['message']
 
    return {
        "statusCode": 200,
        "body": json.dumps({
            "message": f"{message} {first_name}",
            # "location": ip.text.replace("\n", "")
        }),
    }

```

So how do we test this locally well there's a folder here called **events** with an `event.json` file in it and this was created with our template it's got a ton of information. So we just delete that content and create a simple one. 

`event.json`

```
{
  "first_name": "Randika",
  "message": "Hi"
}
```

Now we can test it locally. 

**Step 07:** Npw we want to build it again. 

Run the below command again. 

```
sam build
```

You'll see it invoke the function with sam local invoke let's try that out. 

```
sam local invoke HelloWorldFunction -e events/event.json
```

You see the below output, 

```
PS C:\sam_projects\sam-app2> sam local invoke HelloWorldFunction -e events/event.json
Invoking app.lambda_handler (python3.12)
Local image is up-to-date
Using local image: public.ecr.aws/lambda/python:3.12-rapid-x86_64.

Mounting C:\sam_projects\sam-app2\.aws-sam\build\HelloWorldFunction as /var/task:ro,delegated, inside runtime container
START RequestId: 20e25543-e2c0-4ad7-9193-9a5547529f34 Version: $LATEST
END RequestId: acb97b8f-6f7d-4385-8759-740cbc67ca51
REPORT RequestId: acb97b8f-6f7d-4385-8759-740cbc67ca51  Init Duration: 0.03 ms  Duration: 83.20 ms      Billed Duration: 84 ms  Memory Size: 128 MB     Max Memory Used: 128 MB
{"statusCode": 200, "body": "{\"message\": \"Hi Randika\"}"}
[ERROR] [1704549897802] LAMBDA_RUNTIME Failed to get next invocation. No Response from endpoint
Traceback (most recent call last):
  File "/var/lang/lib/python3.12/site-packages/awslambdaric/lambda_runtime_client.py", line 86, in wait_next_invocation
PS C:\Users\UPERE51\Documents\Personal\Developments\sam_projects\sam-app2> 
```

You can see the below message in the above output. 

`{"statusCode": 200, "body": "{\"message\": \"Hi Randika\"}"}`


That's how we test it. 

**Step 08:**

We want to update the lambda function in AWS, we don't need to do Ssam deploy guided again, because we have this sam config tomo file that says hey here's the stack name here's the s3 bucket here's the region. 

We're just going to do `sam deploy`. 

```
sam deploy
```

Output:

```
PS C:\sam_projects\sam-app2> sam deploy

                Managed S3 bucket: aws-sam-cli-managed-default-samclisourcebucket-xxxxxxx
                A different default S3 bucket can be set in samconfig.toml
                Or by specifying --s3-bucket explicitly.
        Uploading to sam-app2/61f5bc  533019 / 533019  (100.00%)

        Deploying with following values
        ===============================
        Stack name                   : sam-app2
        Region                       : us-west-2
        Confirm changeset            : True
        Disable rollback             : True
        Deployment s3 bucket         : aws-sam-cli-managed-default-samclisourcebucket-xxxxxxx
        Capabilities                 : ["CAPABILITY_IAM"]
        Parameter overrides          : {}
        Signing Profiles             : {}

Initiating deployment
=====================

        Uploading to sam-app2/62.template  516 / 516  (100.00%)


Waiting for changeset to be created..

CloudFormation stack changeset
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------    
Operation                                               LogicalResourceId                                       ResourceType                                            Replacement                                              
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------    
* Modify                                                HelloWorldFunction                                      AWS::Lambda::Function                                   False
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------    
Previewing CloudFormation changeset before deployment
======================================================
Deploy this changeset? [y/N]: y

2024-01-06 19:40:53 - Waiting for stack create/update to complete

CloudFormation events from stack operations (refresh every 5.0 seconds)
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------    
ResourceStatus                                          ResourceType                                            LogicalResourceId                                       ResourceStatusReason
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------    
UPDATE_IN_PROGRESS                                      AWS::CloudFormation::Stack                              sam-app2                                                User Initiated
UPDATE_IN_PROGRESS                                      AWS::Lambda::Function                                   HelloWorldFunction                                      -
UPDATE_COMPLETE                                         AWS::Lambda::Function                                   HelloWorldFunction                                      -
UPDATE_COMPLETE_CLEANUP_IN_PROGRESS                     AWS::CloudFormation::Stack                              sam-app2                                                -
UPDATE_COMPLETE                                         AWS::CloudFormation::Stack                              sam-app2                                                -
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------    


Successfully created/updated stack - sam-app2 in us-west-2
```

We should see in cloud formation the change set created. Let's go to the "sam-app2" and go to change sets and you can see the below

![samapp2cloud](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kbqg0tyeq4tlp3ewbv0y.png)

And that's it that's your workflow for developing lambda functions locally.