Introduction#
GitHub this note shows how to setup lambda with RDS proxy
- Deploy lambda with dependencies
- Lambda connect to RDS inside VPC
- Lambda connect RDS proxy
Setup#
Project structure
|--bin|--lambda-demo.ts|--lambda|--index.py|--package|--requirements.txt|--lib|--lambda-demo-stack.ts|--config.ts
Let install libs in package dir
python3 -m pip install "psycopg[binary,pool]" --target packagepython3 -m pip install psycopg --target package
Logic code to get data from RDS postgresql
# Note: the module name is psycopg, not psycopg3import psycopgimport numpy as npimport jsondbname = "demo"user = "postgresql"password = "Admin2024"host = "database-1.c9y4mg20eppz.ap-southeast-1.rds.amazonaws.com"port = "5432"reponse = []# Connect to an existing databasewith psycopg.connect(f"dbname={dbname} user={user} port={port} host={host} password={password}") as conn:# Open a cursor to perform database operationswith conn.cursor() as cur:# Execute a command: this creates a new tablecur.execute("""SELECT * FROM book """)cur.fetchone()# will return (1, 100, "abc'def")# You can use `cur.fetchmany()`, `cur.fetchall()` to return a list# of several records, or even iterate on the cursorfor record in cur:reponse.append(record)print(record)# Make the changes to the database persistentconn.commit()def handler(context, event):print("Hello")return reponse
Lambda Function#
- Lambda VPC
- Deploy lamdba with dependencies
Let create a lambda which connects to a VPC
export class LambdaDemoStack extends cdk.Stack {constructor(scope: Construct, id: string, props: LambdaProps) {super(scope, id, props)const vpc = cdk.aws_ec2.Vpc.fromLookup(this, 'VpcLookup', {vpcId: 'vpc-0d08a1e78d2e91cb0',vpcName: 'demo'})const securityGroup = cdk.aws_ec2.SecurityGroup.fromSecurityGroupId(this,'SecurityGroupforLambda','sg-014fee8db3f201b09')const subnet = cdk.aws_ec2.Subnet.fromSubnetId(this,'SubnetLambda','subnet-0fc863892f278dabe')new cdk.aws_lambda.Function(this, 'LambdaDemo', {functionName: 'LambdaDemo',handler: 'index.handler',code: cdk.aws_lambda.Code.fromAsset(path.join(__dirname, './../lambda/')),memorySize: 521,timeout: cdk.Duration.seconds(15),runtime: cdk.aws_lambda.Runtime.PYTHON_3_9,vpc: vpc,allowPublicSubnet: true,vpcSubnets: { subnets: [subnet] },securityGroups: [securityGroup],environment: {PYTHONPATH: '/var/task/package',HOST: props.host,PASSWORD: props.password,USER: props.user}})}}
RDS Instance#
Let create a RDS database instance with proxy. First, let create a subnetgroup
const subnetGroup = new cdk.aws_rds.SubnetGroup(this, 'SubnetForRdsDemo', {description: 'subnetgroup for rds demo',vpc: vpc,removalPolicy: cdk.RemovalPolicy.DESTROY,subnetGroupName: 'SubnetForRdsDemo',vpcSubnets: {subnetType: cdk.aws_ec2.SubnetType.PUBLIC}})
Second, create secrete for databse credential and for proxy
const unsafeSecret = new cdk.aws_secretsmanager.Secret(this,'UnsafeSecretDemo',{secretName: 'UnsafeSecretDemo',secretObjectValue: {username: cdk.SecretValue.unsafePlainText('posgresql'),password: cdk.SecretValue.unsafePlainText('Admin2024'),engine: cdk.SecretValue.unsafePlainText('postgres')// resourceId: cdk.SecretValue.unsafePlainText(""),// dbInstanceIdentifier: cdk.SecretValue.unsafePlainText("")}})
And an IAM role for the proxy
const proxyRole = new cdk.aws_iam.Role(this, 'RoleForProxyRdsDemo', {roleName: 'RoleForProxyRdsDemo',assumedBy: new cdk.aws_iam.ServicePrincipal('rds.amazonaws.com')})proxyRole.addToPolicy(new cdk.aws_iam.PolicyStatement({effect: Effect.ALLOW,actions: ['secretsmanager:GetSecretValue'],resources: [secret.secretArn]}))proxyRole.addToPolicy(new cdk.aws_iam.PolicyStatement({effect: Effect.ALLOW,actions: ['kms:Decrypt'],resources: ['*']}))
Third, create a proxy target the database instance
const proxy = new cdk.aws_rds.DatabaseProxy(this, 'RdsProxyDemo', {dbProxyName: 'RdsProxyDemo',proxyTarget: cdk.aws_rds.ProxyTarget.fromInstance(rds),//secrets: [rds.secret!],role: proxyRole,vpc,clientPasswordAuthType: cdk.aws_rds.ClientPasswordAuthType.POSTGRES_MD5,securityGroups: [securityGroup],requireTLS: false})
Finally here is the database instance stack
export class RdsProxyDemo extends cdk.Stack {constructor(scope: Construct, id: string, props: cdk.StackProps) {super(scope, id, props)const vpc = cdk.aws_ec2.Vpc.fromLookup(this, 'VpcLookup', {vpcId: 'vpc-0d08a1e78d2e91cb0',vpcName: 'demo'})const securityGroup = cdk.aws_ec2.SecurityGroup.fromSecurityGroupId(this,'SecurityGroupforLambda','sg-014fee8db3f201b09')const subnet = cdk.aws_ec2.Subnet.fromSubnetId(this,'SubnetLambda','subnet-0fc863892f278dabe')const unsafeSecret = new cdk.aws_secretsmanager.Secret(this,'UnsafeSecretDemo',{secretName: 'UnsafeSecretDemo',secretObjectValue: {username: cdk.SecretValue.unsafePlainText('posgresql'),password: cdk.SecretValue.unsafePlainText('Admin2024'),engine: cdk.SecretValue.unsafePlainText('postgres')// resourceId: cdk.SecretValue.unsafePlainText(""),// dbInstanceIdentifier: cdk.SecretValue.unsafePlainText("")}})const credentials =cdk.aws_rds.Credentials.fromGeneratedSecret('postgresql')const subnetGroup = new cdk.aws_rds.SubnetGroup(this, 'SubnetForRdsDemo', {description: 'subnetgroup for rds demo',vpc: vpc,removalPolicy: cdk.RemovalPolicy.DESTROY,subnetGroupName: 'SubnetForRdsDemo',vpcSubnets: {subnetType: cdk.aws_ec2.SubnetType.PUBLIC}})const rds = new cdk.aws_rds.DatabaseInstance(this, 'RdsDbInstanceDemo', {databaseName: 'RdsDbInstanceDemo',engine: cdk.aws_rds.DatabaseInstanceEngine.postgres({version: cdk.aws_rds.PostgresEngineVersion.VER_15_4}),instanceType: cdk.aws_ec2.InstanceType.of(cdk.aws_ec2.InstanceClass.T3,cdk.aws_ec2.InstanceSize.MICRO),// credentials: cdk.aws_rds.Credentials.fromGeneratedSecret("postgresql"),// credentials: cdk.aws_rds.Credentials.fromSecret(secret),// credentials : {// username: "postgresql",// password: cdk.SecretValue.unsafePlainText("Admin2024")// },credentials: credentials,vpc: vpc,subnetGroup: subnetGroup,// vpcSubnets: {subnetType: cdk.aws_ec2.SubnetType.PUBLIC},securityGroups: [securityGroup],port: 5432})const proxyRole = new cdk.aws_iam.Role(this, 'RoleForProxyRdsDemo', {roleName: 'RoleForProxyRdsDemo',assumedBy: new cdk.aws_iam.ServicePrincipal('rds.amazonaws.com')})proxyRole.addToPolicy(new cdk.aws_iam.PolicyStatement({effect: Effect.ALLOW,actions: ['secretsmanager:GetSecretValue'],resources: ['*']}))proxyRole.addToPolicy(new cdk.aws_iam.PolicyStatement({effect: Effect.ALLOW,actions: ['kms:Decrypt'],resources: ['*']}))const proxy = new cdk.aws_rds.DatabaseProxy(this, 'RdsProxyDemo', {dbProxyName: 'RdsProxyDemo',proxyTarget: cdk.aws_rds.ProxyTarget.fromInstance(rds),//secrets: [rds.secret!],role: proxyRole,vpc,clientPasswordAuthType: cdk.aws_rds.ClientPasswordAuthType.POSTGRES_MD5,securityGroups: [securityGroup],requireTLS: false})}}
Troubleshooting#
Connect to default database name
psql -h PROXY_ENDPOINT -p 5432 -U postgresql -d postgres