Create and deploy a lambda written in Python accessing PostgreSQL or Redshift

In this blog I will demonstrate the creation of a Lambda written in Python to access a Postgres or Redshift database. As both these database engines are based on Postgres, the same process will work.

In order to access Postgres from Lambda we need to create and deploy a Lambda layer containing Psycopg, the most popular PostgreSQL database adapter for the Python programming language. I will be deploying version 3 of Psycopg.

Prerequisites
The first step is to create the software in a zip file required by the layer. A typical approach is to perform these steps on an EC2. This does require that an EC2 be deployed with the required permissions (IAM Role). Instead, I will be performing these commands in a Cloudshell environment. If you are connected to the AWS Management Console, either search for “Cloudshell” or click on the icon usually located in the top right corner of the web page.

Important note : This blog is using Python version 3.12 in all the commands. If you need to use a different version, change “3.12” to the version you need.

Install Psycopg
Make a directory to house the software. It is important that this directory structure be exactly as defined below as we will be zipping this directory to create the layer.

mkdir -p dc-layer/python

And then change directory to the above directory

cd dc-layer

Install the software with the command

pip3 install psycopg[binary] \
		--python-version 3.12 \
		--only-binary=:all: \
		--platform manylinux2014_x86_64 \
		-t dc-layer/python

The following directory structure and files should exist

/home/cloudshell-user $ pwd
/home/cloudshell-user/dc-layer/python
/home/cloudshell-user $ ls -l
total 160
drwxr-xr-x. 6 cloudshell-user cloudshell-user   4096 Jul  8 20:37 psycopg
drwxr-xr-x. 2 cloudshell-user cloudshell-user   4096 Jul  8 20:37 psycopg-3.2.1.dist-info
drwxr-xr-x. 3 cloudshell-user cloudshell-user   4096 Jul  8 20:37 psycopg_binary
drwxr-xr-x. 2 cloudshell-user cloudshell-user   4096 Jul  8 20:37 psycopg_binary-3.2.1.dist-info
drwxr-xr-x. 2 cloudshell-user cloudshell-user   4096 Jul  8 20:37 psycopg_binary.libs
drwxr-xr-x. 2 cloudshell-user cloudshell-user   4096 Jul  8 20:37 __pycache__
drwxr-xr-x. 2 cloudshell-user cloudshell-user   4096 Jul  8 20:37 typing_extensions-4.12.2.dist-info
-rw-r--r--. 1 cloudshell-user cloudshell-user 134451 Jul  8 20:37 typing_extensions.py

Zip the software for the lambda layer
Go up one level to the directory that contains the “python” directory and zip the software installed in the previous step with

zip -r layer.zip python

The directory should now contain a zip file

/home/cloudshell-user $ pwd; ls -l
/home/cloudshell-user/dc-layer
total 4668
-rw-r--r--. 1 cloudshell-user cloudshell-user 4772949 Jul  8 20:43 layer.zip
drwxr-xr-x. 9 cloudshell-user cloudshell-user    4096 Jul  8 20:37 python

Publish the layer
Publish the layer using AWS CLI with the below command

aws lambda publish-layer-version --layer-name dc-psycopg3-layer \
    --zip-file fileb://layer.zip \
    --compatible-runtimes python3.12 \
    --compatible-architectures "x86_64"

Confirm that the layer was published
From the AWS Management Console, navigate to the Lambda Layers page and confirm that the above layer is present.

Create a Lambda function
Create a new lambda function using Python 3.12

Add the layer previously uploaded to the function’s definition

Lambda function – sample code
Below is sample code that connects to a Redshift cluster and performs a select on a table

import logging
import psycopg
from psycopg import OperationalError
psycopg._encodings._py_codecs["UNICODE"] = "utf-8"
psycopg._encodings.py_codecs.update(
    (k.encode(), v) for k, v in psycopg._encodings._py_codecs.items()
)

logger = logging.getLogger()
logger.setLevel(logging.INFO)

def lambda_handler(event, context):

    print('**** Connecting to Redshift')
    my_hostname = 'my-cluster.us-east-1.redshift.amazonaws.com'
    my_database = 'my-databse'
    my_user     = 'my-database-user'
    my_password = 'my-db-user-password'
    my_port     =  5439

    connection_args = 'host={} port={} dbname={} user={} password={}'.format(
        my_hostname,
        my_port,
        my_database,
        my_user,
        my_password
    )

    try:
        logger.info("Establishing connection to Redshift")
        conn = psycopg.connect(connection_args)
        logger.info("SUCCESS: Connection to Redshift succeeded!")
    except psycopg.Error as e:
        logger.error("ERROR: Unexpected error: Could not connect to Redshift.")
        logger.error(e)


    sql_text = "select col_01, col_02 from my_schema.my_table where col_01 = 'dc-test'"
    print(sql_text)

    try:
        cur = conn.cursor()
        cur.execute(sql_text)
        for row in cur.fetchall():
            col_01 = row[0]
            col_02 = row[1]
            print(col_01, col_02)
        cur.close()
    except psycopg.Error as e:
        print('SQL failed. Failing text is ', sql_text)
        print(f"Error from database",e)

Author: Dean Capps

Database consultant at Amazon Web Services.

142
51
73
77