CodeWithYou

Connecting AWS Lambda with Amazon RDS using AWS CDK and Node.js

Published on
Authors
Connecting AWS Lambda with Amazon RDS using AWS CDK and Node.js
Photo by mollyblackbird

AWS Lambda is a popular serverless compute service that lets you run code in response to events without having to manage the underlying infrastructure. Amazon RDS is a managed relational database service that makes it easy to set up, operate, and scale a relational database in the cloud. In this tutorial, we will explore how to connect an AWS Lambda function to an RDS instance to perform database operations.

Key Points

Here are the key points to keep in mind when connecting an AWS Lambda function to an RDS instance:

Accessing RDS over the Internet or VPC

An RDS instance can be accessed over the Internet using a public IP address or DNS name, or over a VPC using a private IP address. If your RDS instance is publicly accessible, you can use the public IP address or DNS name to connect to it. If your Lambda function is running inside a VPC, you can use a private IP address to connect to the RDS instance. Here's an example of how to create an RDS instance that is publicly accessible:

const rds = new aws.RDS({
  engine: 'mysql',
  instanceClass: 'db.t3.micro',
  allocatedStorage: 10,
  dbName: 'my_database',
  masterUsername: 'admin',
  masterUserPassword: 'my_password',
  publiclyAccessible: true,
  vpcSecurityGroupIds: [mySecurityGroup.securityGroupId],
})

Here, we're creating an RDS instance using the aws-sdk in Node.js, and setting the publiclyAccessible property to true to allow connections from the Internet. We also specify a security group ID to allow inbound traffic from our Lambda function's security group.

Connecting Lambda to RDS inside VPC

If your Lambda function is running inside a VPC, you can use a private IP address to connect to the RDS instance. Here's an example of how to create a Lambda function inside a VPC:

const myVpc = new aws.ec2.Vpc('my-vpc', {
  cidrBlock: '10.0.0.0/16',
  tags: {
    Name: 'My VPC',
  },
})

const mySubnet = new aws.ec2.Subnet('my-subnet', {
  cidrBlock: '10.0.0.0/24',
  vpcId: myVpc.id,
  availabilityZone: 'us-east-1a',
  tags: {
    Name: 'My Subnet',
  },
})

const mySecurityGroup = new aws.ec2.SecurityGroup('my-security-group', {
  vpcId: myVpc.id,
  ingress: [
    {
      protocol: 'tcp',
      fromPort: 3306,
      toPort: 3306,
      cidrBlocks: [myVpc.vpcCidrBlock],
    },
  ],
  tags: {
    Name: 'My Security Group',
  },
})

const myLambdaFunction = new aws.lambda.Function('my-lambda-function', {
  runtime: 'nodejs14.x',
  code: new aws.lambda.AssetCode('path/to/lambda/code'),
  handler: 'index.handler',
  vpcConfig: {
    subnetIds: [mySubnet.id],
    securityGroupIds: [mySecurityGroup.id],
  },
})

Here, we're creating a VPC with a subnet and a security group that allows inbound traffic on port 3306 (for MySQL connections). We're then creating a Lambda function that is associated with the VPC and the security group.

Advertisement

Making RDS Publicly Accessible

If your Lambda function is not running inside a VPC, the RDS instance must be publicly accessible or located in a public subnet that has a route to the Internet. Here's an example of how to create an RDS instance in a public subnet:

const myVpc = new aws.ec2.Vpc('my-vpc', {
  cidrBlock: '10.0.0.0/16',
  tags: {
    Name: 'My VPC',
  },
})

const myPublicSubnet = new aws.ec2.Subnet('my-public-subnet', {
  cidrBlock: '10.0.1.0/24',
  vpcId: myVpc.id,
  mapPublicIpOnLaunch: true,
  availabilityZone: 'us-east-1a',
  tags: {
    Name: 'My Public Subnet',
  },
})

const mySecurityGroup = new aws.ec2.SecurityGroup('my-security-group', {
  vpcId: myVpc.id,
  ingress: [
    {
      protocol: 'tcp',
      fromPort: 3306,
      toPort: 3306,
      cidrBlocks: [myVpc.vpcCidrBlock],
    },
  ],
  tags: {
    Name: 'My Security Group',
  },
})

const rdsInstance = new aws.rds.Instance('my-rds-instance', {
  engine: 'mysql',
  instanceClass: 'db.t3.micro',
  allocatedStorage: 10,
  dbName: 'my_database',
  masterUsername: 'admin',
  masterUserPassword: 'my_password',
  vpcSecurityGroupIds: [mySecurityGroup.securityGroupId],
  subnetIds: [myPublicSubnet.id],
})

Here, we're creating a VPC with a public subnet that has a route to the Internet, and a security group that allows inbound traffic on port 3306. We're then creating an RDS instance that is associated with the VPC, the security group, and the public subnet.

Using IAM Role to Connect to RDS

Instead of hard-coding the database credentials in your Lambda function, you can use an IAM role to provide temporary credentials to access the RDS instance. Here's an example of how to create an IAM role for your Lambda function to access the RDS instance:

const rdsRole = new aws.iam.Role('rds-lambda-role', {
  assumeRolePolicy: JSON.stringify({
    Version: '2012-10-17',
    Statement: [
      {
        Effect: 'Allow',
        Principal: {
          Service: 'lambda.amazonaws.com',
        },
        Action: 'sts:AssumeRole',
      },
    ],
  }),
})

const rdsPolicy = new aws.iam.Policy('rds-lambda-policy', {
  policy: JSON.stringify({
    Version: '2012-10-17',
    Statement: [
      {
        Effect: 'Allow',
        Action: ['rds-db:connect'],
        Resource: [rdsInstance.arn],
      },
    ],
  }),
})

new aws.iam.RolePolicyAttachment('rds-lambda-role-policy', {
  policyArn: rdsPolicy.arn,
  role: rdsRole.name,
})

const myLambdaFunction = new aws.lambda.Function('my-lambda-function', {
  runtime: 'nodejs14.x',
  code: new aws.lambda.AssetCode('path/to/lambda/code'),
  handler: 'index.handler',
  role: rdsRole.arn,
})

Here, we're creating an IAM role that allows the Lambda function to assume the role and connect to the RDS instance. We're also attaching a policy to the role that allows the Lambda function to connect to the RDS instance using the rds-db:connect action. Finally, we're creating a Lambda function that is associated with the IAM role.

Sample Lambda Function Code

Here's an example of how to write a Lambda function in Node.js that connects to an RDS instance:

const AWS = require('aws-sdk')
const mysql = require('mysql')

exports.handler = async (event) => {
  // Read environment variables
  const dbHost = process.env.DB_HOST
  const dbUser = process.env.DB_USER
  const dbPassword = process.env.DB_PASSWORD
  const dbName = process.env.DB_NAME

  // Create a MySQL connection pool
  const pool = mysql.createPool({
    host: dbHost,
    user: dbUser,
    password: dbPassword,
    database: dbName,
  })

  // Perform a SQL query using the connection pool
  const query = 'SELECT * FROM my_table'
  const results = await new Promise((resolve, reject) => {
    pool.query(query, (error, results) => {
      if (error) {
        reject(error)
      } else {
        resolve(results)
      }
    })
  })

  // Format the results as a JSON object
  const response = {
    statusCode: 200,
    headers: {
      'Content-Type': 'application/json',
    },
    body: JSON.stringify(results),
  }

  return response
}

This Lambda function reads the RDS connection details from environment variables and uses them to create a MySQL connection pool. It then performs a SQL query and formats the results as a JSON object that is returned as the response.

Conclusion

We've also provided code samples for creating a VPC and security group using the AWS CDK and for writing a Lambda function that connects to an RDS instance using the mysql2 library.

Remember that while VPCs provide an added layer of security, they also add complexity to your architecture. Make sure to carefully design and test your VPC and security group configurations to ensure that they meet your specific needs.

Finally, it's important to regularly monitor and review your VPC and security group settings to ensure that they remain up-to-date and secure. By following these best practices, you can ensure that your Lambda function and RDS instance are always properly secured and functioning as intended.

Advertisement