Set up Aurora Serverless and RDS Proxy with AWS CDK

It has been challenging to set up AWS aurora which has a serverless instance instead of provisioned instance which can also be configured with an RDS proxy which then can be used by an application. At the time of writing this, I could not find a way can set this up straight using AWS CDK, hence thought of sharing this idea which can be useful.

At the time of writing this, while using CDK, I could set up a “provisioned instance” along with “RDS proxy” but if I set up a “serverless instance” then I cannot set up an “RDS proxy”. So I found a workaround as mentioned below which might work for you well.

Workaround steps :

  1. disable “instanceType” here in the stack code

  2. cdk deploy

  3. change instanceType to serverless manually on the AWS console

  4. enable instanceType here

  5. cdk deploy again to make sure the instance type remains serverless

Here is my code for CDK:

const {
  Stack,
  Duration,
  CfnOutput,
  RemovalPolicy,
  InstanceType,
  Aspects,
} = require("aws-cdk-lib");

// const sqs = require('aws-cdk-lib/aws-sqs');
const cognito = require("aws-cdk-lib/aws-cognito");
const s3 = require("aws-cdk-lib/aws-s3");
const rds = require("aws-cdk-lib/aws-rds");
const ec2 = require("aws-cdk-lib/aws-ec2");
const ssm = require("aws-cdk-lib/aws-ssm");
const secrets = require("aws-cdk-lib/aws-secretsmanager");

class TestAppInfraStack extends Stack {
  /**
   *
   * @param {Construct} scope
   * @param {string} id
   * @param {StackProps=} props
   */
  constructor(scope, id, props) {
    super(scope, id, props);

    //  create the VPC
    // NAT Gateway for cognito access
    const vpc = new ec2.Vpc(this, "TestAppApp", {
      cidr: "10.0.0.0/16",
      natGateways: 1,
      maxAzs: 3,
      subnetConfiguration: [
        {
          name: "public-subnet-1",
          subnetType: ec2.SubnetType.PUBLIC,
          cidrMask: 24,
        },
        {
          name: "private-subnet-1",
          subnetType: ec2.SubnetType.PRIVATE_WITH_NAT,
          cidrMask: 24,
        },
      ],
    });

    // Add an interface endpoint
    vpc.addInterfaceEndpoint("SecretManagerEndpoint", {
      service: ec2.InterfaceVpcEndpointAwsService.SECRETS_MANAGER,
    });

    // We need this security group to add an ingress rule and allow our lambda to query the proxy
    let lambdaToRDSProxyGroup = new ec2.SecurityGroup(
      this,
      "Lambda to RDS Proxy Connection",
      {
        vpc,
      }
    );
    // We need this security group to allow our proxy to query our DB Instance
    let dbConnectionGroup = new ec2.SecurityGroup(
      this,
      "Proxy to DB Connection",
      {
        vpc,
      }
    );
    dbConnectionGroup.addIngressRule(
      dbConnectionGroup,
      ec2.Port.tcp(5432),
      "allow db connection"
    );
    dbConnectionGroup.addIngressRule(
      lambdaToRDSProxyGroup,
      ec2.Port.tcp(5432),
      "allow lambda connection"
    );

    const databaseUsername = "TestAppaurora";

    // Dynamically generate the username and password, then store in secrets manager
    const databaseCredentialsSecret = new secrets.Secret(
      this,
      "DBCredentialsSecret",
      {
        secretName: id + "-rds-credentials",
        generateSecretString: {
          secretStringTemplate: JSON.stringify({
            username: databaseUsername,
          }),
          excludePunctuation: true,
          includeSpace: false,
          generateStringKey: "password",
        },
      }
    );

    new ssm.StringParameter(this, "DBCredentialsArn", {
      parameterName: "rds-credentials-arn",
      stringValue: databaseCredentialsSecret.secretArn,
    });

    // Create Db Cluster with rds proxy
    /*
    1. disable instanceType here in the code
    2. cdk deploy
    3. change instanceType to serverless manually on AWS console
    4. enable instanceType here
    5. cdk deploy again to make sure instance type remains serverless
    */
    const dbCluster = new rds.DatabaseCluster(this, "TestAppAurora", {
      credentials: rds.Credentials.fromSecret(databaseCredentialsSecret),
      engine: rds.DatabaseClusterEngine.auroraPostgres({
        version: rds.AuroraPostgresEngineVersion.VER_13_6,
      }),
      instances: 1,
      instanceProps: {
        vpc: vpc,
        autoMinorVersionUpgrade: true,
        publiclyAccessible: true,
        securityGroups: [dbConnectionGroup],
        vpcSubnets: vpc.selectSubnets({
          subnetType: ec2.SubnetType.PUBLIC,
        }),
        instanceType: "serverless", // disable and enable this line to make it work as mentioned above
      },
      defaultDatabaseName: "TestApp",
      removalPolicy: RemovalPolicy.DESTROY,
    });

    // Create an RDS Proxy
    const proxy = dbCluster.addProxy(id + "-proxy", {
      secrets: [databaseCredentialsSecret],
      debugLogging: true,
      vpc,
      securityGroups: [dbConnectionGroup],
    });
  }
}

module.exports = { TestAppInfraStack };

Hope this is helpful….

happy coding!

Enjoyed this article?

Share it with your network to help others discover it

Continue Learning

Discover more articles on similar topics