How to Setup an AWS Lambda Function with Python and Connect with an Amazon RDS Database.
Tags: cloudskills, lambda, aws
Published: 2023-09-11 11:56:25

## Introduction

AWS Lambda introduces a concept known as _serverless infrastructure_. It is serverless in the sense that you don't have to worry about managing or provisioning of servers. They can automate operational processes, as well as complex application processes.

AWS Lambda's main resource are Lambda functions which are used to perform specific tasks. 

## Deploying a Lambda Function with Python

### Prerequisites
To follow this tutorial, you will need an AWS account. If you do not have one yet, you can follow this guide on how to [set-up your environment.](https://aws.amazon.com/getting-started/guides/setup-environment/)
The steps described here are under the Free Tier, so feel free to play around.

### Create a Lambda Function
- Visit the [AWS Management Console](https://console.aws.amazon.com/console/home?region=us-east-1) and under **Services** select the **Compute** tab and you'll find the **Lambda** service. Select it to open the Lambda Console.

![Lambda Console](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zdqau3zpgsk8xx5er290.png)

- In the Lambda console, navigate to **Functions** on the left pane and select it. This window shows you all the Lambda functions you have previously create and if there are none yet, no data would be displayed in the table.
- At the top of this table, click the **Create Function** button to start creating your Lambda function.

![Create Function](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/98javwhlezuv3huzlu97.png)

- There are 3 options you could choose from to create a Lambda function; author from scratch, use a blueprint to build from sample code with configuration presets, and deploying the function from a container image.
For this tutorial, we will use the **Author from scratch** option
![Creation mode](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/h99tktzn9z8xjbnxoi8k.png)

- In the **Basic Information** section, set the **Function Name**, choose the language version to use for your function for **Runtime**, select Architecture making sure it's compatible with the environment where you will build your deployment package(if applicable), and you can change the permissions for the execution role. 
We will use **Python 3.9** as our runtime environment and **x86_64** architecture.

![Basic Info Config](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5vxogz87bf3b620lgbpm.png)


- In **Advanced Settings**, you could enable Code signing to ensure the integrity your code, enable function URL to make your function accessible over http(s), enable tags or VPCs.
For this tutorial, we will leave all this options unchecked and click the **Create Function** button to create the lambda function.

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

Lambda creates your function. You can use the console to modify your function's code or you could also upload from an S3 location or zip. 
The function was created with some basic code that returns a status code of 200 and "Hello from Lambda" text. 

The lambda function handler [in our case **lambda_handler()**] is the method that processes events. The lambda runtime passes two arguments to the function handler;
      - Event Object: json formatted and contains data for the lambda function to process
      - Context Object: provide methods and properties that provide information about the invocation, function and runtime environment and it is passed to the function by Lambda at runtime.

![function overview](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2inyndykekc54766d146.png)

![Boiler code](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3tdoppd4s3ux52kjkhki.png)

To test this, choose the **Test** tab. To invoke your function without saving the test event, click on **Invoke**, lambda preserves the event only for as long as the session.
Create a saved test by filling in the event details and choosing **Save** then on **Test** to invoke the function.
If you have a saved test even, you can also edit it.

![Create test](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/nqtd9pbx2eksztjcyx9q.png)

View the results of the test in the **Execution results** tab. We can see that our function executed successfully and returned the desired result.

![execution results](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/lmjvaak6tlgpo8ounvgt.png)

### Include Python Dependencies to Lambda function: Deployment Packages

```
import json
import mysql.connector;

def lambda_handler(event, context):
    # TODO implement
    return {
        'statusCode': 200,
        'body': json.dumps('Hello from Lambda!')
    }
```
If we include the above code in our console, and test it, an error is returned stating the mysql module can't be found. This is because lambda is not prepackaged with all libraries and does not install any. 


![Module error](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vqgbqk0i7fx2nsxr5los.png)

Hence, if your code contains dependencies, you have to include them by using a deployment package or a lambda layer. We will be looking at including these dependencies using a **.zip** deployment package.

**Creating the Deployment Package**

- Navigate to the the directory containing your lambda function on your computer, **lambda_function.py**, in this case firstFunction.

```
cd firstFunction
```

- Create a directory where you would install your dependencies

```
mkdir packages
```
- Install your dependencies in this directory. In this instance, we will install **mysql-connector-python** using pip, which enables connection to a mysql database. If you created your packages, simply save them to the **packages** folder.

```
pip install --target ./packages mysql-connector-python
```

- Create a zip file with the **packages** folder at the root

```
cd packages
zip -r ../deployment_package.zip .
```

- Add your lambda function to the root of the deployment package. The deployment package has to have a flat directory structure with everything at the root, if not, Lambda won't be able to run the code.

```
cd ..
zip deployment_package.zip lambda_function.py
```

Under the **Code** tab, select **Upload** and click on **.zip file** and select the **deployment_package.zip** file you created earlier.
For packages bigger than 50MB, it is advised to upload it to an S3 bucket then connect to the S3 location.

This now works seamlessly.

**Note:** You could still do changes to your function directly on the console. Always click on **Deploy** to save your changes before executing.

## Connecting an RDS database to the Lambda Function

To get started with Amazon RDS for MySQL, visit this [article](https://dev.to/giftcup/how-to-deploy-a-managed-mysql-instance-in-an-amazon-rds-database-3dpm).

After you have created your MySQL instance, include the Lambda function in the same VPC as the MySQL RDS instance in order for them to communicate. 

In order to add the VPC to the Lambda function, you first need to modify the permissions attached to the role managing the function. To do this, open the IAM service in a new tab, click on **Dashboard** then on **Roles**. 


![Find IAM service](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0x8260ozsgnaid06drl8.png) ![Roles](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/g0ao1rkpsrtzpla60lfk.png)

Select the role that manages your lambda function, the name would have your function name and some random characters. Create a policy that enables it to create and delete network interfaces. We will **AWSLambdaVPCAccessExecutionRole** policy for that, but you also have the ability to write your own policies.

Edit the Lambda function's VPC by navigating to the **Configuration** Tab, then select **VPC** on the side menu and click **Edit** to add or edit a VPC. 

![Edit VPC](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/a5g43izx2iahtyodo1e8.png)

![add VPC](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/fcowsky4apfr4850z8jw.png)


Return to your RDS instance's dashboard. Click on the **Actions** drop down and select **Set up Lambda connection**.

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

In the **Select Lambda function** section, select your lambda function from the dropdown.

In the **RDS Proxy** section, leave the **Connect using RDS Proxy** unchecked to stay within Free Tier.

You can use an RDS proxy to simplify your database connection and minimize your application from disruptions caused by database unavailability. 

Click on **Set up** to create the connection to the lambda function.


![select function](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5nytnp46fn3nt3moe5r5.png)

Add the mysql connection string to your python handler function. 

```
mydb = mysql.connector.connect (
           host = "<rds_instance_endpoint>",
           port = <rds_instance_port>,
           user = "<master_username>",
           password = "<master_password>",
           database = "<database_name>"
          )
```

If you used an RDS proxy, host url should be the endpoint of the proxy instead.

The database_name is the name of any database running on the RDS instance in use. You can create one by connecting to this instance from the mysql CLI or an editor and running the query to create a database.

With all this in place, you will be able to query your database from the lambda function.

## Troubleshooting
**Runtime Error: Handler Not Found**

```
"errorMessage": "Handler 'lambda_handler' missing on module 'lambda_function'",
  "errorType": "Runtime.HandlerNotFound"
```

This error occurs when Lambda can't resolve the Handler. Navigate to **Runtime Settings** in your function's dashboard and click **Edit** to modify the Handler. The Handler should have the format: **fileName.functionName**

![runtime settings](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/yyj81l5ny0aclwzxff7d.png)

![edit handler](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/bej7ocqlsshreueoueu5.png)