<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: shinya</title>
    <description>The latest articles on DEV Community by shinya (@shinyat).</description>
    <link>https://dev.to/shinyat</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1100430%2Faed145b1-aad0-4c8d-ab28-b0268d326315.jpg</url>
      <title>DEV Community: shinya</title>
      <link>https://dev.to/shinyat</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/shinyat"/>
    <language>en</language>
    <item>
      <title>Connect lambda and RDS with RDS proxy using AWS CDK(ver.2) python</title>
      <dc:creator>shinya</dc:creator>
      <pubDate>Tue, 13 Jun 2023 06:56:21 +0000</pubDate>
      <link>https://dev.to/shinyat/connect-lambda-and-rds-with-rds-proxy-using-aws-cdkver2-python-4p5l</link>
      <guid>https://dev.to/shinyat/connect-lambda-and-rds-with-rds-proxy-using-aws-cdkver2-python-4p5l</guid>
      <description>&lt;p&gt;Since I had the opportunity to develop with AWS CDK for a project, I would share the knowledge I gained from the development.&lt;br&gt;
In this article, I will explain how to use CDK (ver. 2) Python to connect from an AWS Lambda to RDS in a private subnet via RDS Proxy.&lt;/p&gt;

&lt;p&gt;See Github for full code.&lt;br&gt;
&lt;a href="https://github.com/ShinyaT1994/cdk-lambda-rds-proxy.git" rel="noopener noreferrer"&gt;Github here&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fm4irba8b80vvbcjvb23s.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fm4irba8b80vvbcjvb23s.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Prerequisites.
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;AWS CDK v.2 must be installed&lt;/li&gt;
&lt;li&gt;Python 3.x must be installed&lt;/li&gt;
&lt;li&gt;AWS account and AWS CLI must be set up.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Cloud9 can be used to pass the above, so the content of this article was created using Cloud9.&lt;/p&gt;
&lt;h2&gt;
  
  
  Build procedure
&lt;/h2&gt;
&lt;h3&gt;
  
  
  Initialize CDK application
&lt;/h3&gt;

&lt;p&gt;First, initialize the CDK application.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;mkdir &lt;/span&gt;cdk-lambda-rds-proxy
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;cdk-lambda-rds-proxy
&lt;span class="nv"&gt;$ &lt;/span&gt;cdk init &lt;span class="nt"&gt;--language&lt;/span&gt; python
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. install necessary packages
&lt;/h3&gt;

&lt;p&gt;Here we activate the .venv created when we initialized the CDK app.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;source&lt;/span&gt; .venv/bin/activate
&lt;span class="nv"&gt;$ &lt;/span&gt;pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; requirements.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. create the stack
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;from constructs import Construct
from aws_cdk import (
    Stack,
    RemovalPolicy,
    aws_ec2 as ec2,
    aws_lambda as _lambda,
    aws_rds as rds,
    aws_secretsmanager as sm,
)


class CdkLambdaRdsProxyStack(Stack):
    def __init__(self, scope: Construct, id: str, **kwargs) -&amp;gt; None:
        super().__init__(scope, id, **kwargs)

        # Create VPC
        vpc = ec2.Vpc(self, "MyVPC",
            max_azs=2
        )

        # Create security group for RDS
        rds_security_group = ec2.SecurityGroup(
            self, 
            "RdsSecurityGroup",
            vpc=vpc,
            description="Allow Lambda access to RDS"
        )

        # Create RDS instance
        rds_instance = rds.DatabaseInstance(
            self, 
            "RDS",
            engine=rds.DatabaseInstanceEngine.postgres(
                version=rds.PostgresEngineVersion.VER_14_5
            ),
            vpc=vpc,
            security_groups=[rds_security_group],
            instance_type=ec2.InstanceType.of(
                ec2.InstanceClass.BURSTABLE3, ec2.InstanceSize.SMALL
            ),
            allocated_storage=20,
            removal_policy=RemovalPolicy.DESTROY,
            deletion_protection=False,
            database_name="MyDatabase",
            auto_minor_version_upgrade=True,
            multi_az=False
        )

        # Create security group for lambda
        lambda_security_group = ec2.SecurityGroup(
            self, 
            "LambdaSecurityGroup",
            vpc=vpc,
            description="Allow Lambda to access RDS Proxy"
        )

        # Create RDS Proxy
        rds_proxy = rds.DatabaseProxy(
            self, 
            "RDSProxy",
            proxy_target=rds.ProxyTarget.from_instance(rds_instance),
            vpc=vpc,
            security_groups=[lambda_security_group],
            db_proxy_name="MyRDSProxy",
            debug_logging=False,
            secrets=[rds_instance.secret],
            require_tls=True
        )

        # lambda layer of psycopg2
        psycopg2_layer = _lambda.LayerVersion.from_layer_version_arn(
            self, 
            "Psycoog2Layer",
            'arn:aws:lambda:ap-northeast-1:898466741470:layer:psycopg2-py38:1'
        )

        # Create lambda function
        lambda_function = _lambda.Function(
            self, 
            "LambdaFunction",
            runtime=_lambda.Runtime.PYTHON_3_8,
            handler="lambda_function.handler",
            code=_lambda.Code.from_asset("cdk_lambda_rds_proxy/lambda"),
            layers=[psycopg2_layer],
            vpc=vpc,
            security_groups=[lambda_security_group],
            vpc_subnets=ec2.SubnetSelection(subnets=vpc.private_subnets),
            environment={
                "DB_SECRET_ARN": rds_instance.secret.secret_arn,
                "DB_PROXY_ENDPOINT": rds_proxy.endpoint
            }
        )

        # Policies
        rds_instance.connections.allow_from(
            lambda_security_group,
            port_range=ec2.Port.tcp(5432)
        )
        rds_proxy.connections.allow_from(
            lambda_security_group,
            port_range=ec2.Port.tcp(5432)
        )

        rds_instance.secret.grant_read(lambda_function)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  4. Create a Lambda function
&lt;/h3&gt;

&lt;p&gt;Create a lambda folder, create a lambda_function.py file in it, and write the following&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import os
import json
import psycopg2
import boto3
from botocore.exceptions import ClientError

def get_db_credentials(secret_arn):.
    secrets_manager = boto3.client("secretsmanager")
    try:: secrets_manager = boto3.client("secretsmanager")
        response = secrets_manager.get_secret_value(SecretId=secret_arn)
        return json.loads(response["SecretString"])
    except ClientError as e: print(e)
        print(e)
        return None

def handler(event, context):
    secret_arn = os.environ["DB_SECRET_ARN"]]
    db_proxy_endpoint = os.environ["DB_PROXY_ENDPOINT"]]

    credentials = get_db_credentials(secret_arn)

    if not credentials: return { if not credentials
        return {
            "statusCode": 500, "body": "Error retrieving
            "body": "Error retrieving database credentials"
        }

    connection_string = f "dbname={credentials['dbname']} user={credentials['username']} password={credentials['password']} host={db_proxy_ endpoint} port={credentials['port']} sslmode=require"

    try:.
        conn = psycopg2.connect(connection_string)
        cursor = conn.cursor()
        cursor.execute("SELECT version();")
        result = cursor.fetchone()

        return {
            "statusCode": 200,.
            "body": f "Database version: {result[0]}"
        }

    except Exception as e:.
        print(e)
        return {
            "statusCode": 500, "body": "Error connecting to the database" }
            "body": "Error connecting to the database"
        }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  5. Deploy the CDK app
&lt;/h3&gt;

&lt;p&gt;Now that the Stack is ready, deploy it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;cdk deploy
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once deployed, the Lambda function will connect to the RDS database to retrieve version information.&lt;/p&gt;

&lt;p&gt;↓↓ Actual connection confirmed.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F30obrffin6w1zzqiszmb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F30obrffin6w1zzqiszmb.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;This article described how to use CDK(ver.2) Python to connect from a Lambda function to an RDS instance in a private subnet via RDS Proxy.&lt;br&gt;
It seems to be an anti-pattern to connect Lambda and RDS directly, so please refer to this when configuring Lambda + RDS.&lt;/p&gt;

&lt;h2&gt;
  
  
  Reference
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://docs.aws.amazon.com/cdk/api/v2/docs/aws-construct-library.html" rel="noopener noreferrer"&gt;AWS CDK API Reference&lt;/a&gt;&lt;/p&gt;

</description>
      <category>aws</category>
      <category>lambda</category>
      <category>rds</category>
      <category>cdk</category>
    </item>
  </channel>
</rss>
