Thomas Step

← Blog

I recently launched a Slack app to help with channel bloat! Simply installing it would help me out a bunch. I have 2/10 installations required to submit my app to the Slack Marketplace. Thanks for helping me reach that goal.

CloudFormation Example for Serverless RDS

I have been updating my CloudFormation reference repo on GitHub lately, and I wanted to bring up one of the newly added templates. This template builds a serverless RDS instance, generates a password for that instance, adds a secret to Secrets Manager, and allows for an instance to be built off of an existing snapshot. I also took a screen capture of me building and debugging that template that is available on YouTube if you care to watch how I built it.

I’ll start by mentioning that there are conditions in this template that determine whether brand new database instances will be launched or if those instances will be based on a snapshot (I recommend looking at the template if this is confusing), but I will also include the snippet that sets those conditions up.

Parameters:
  SnapshotArn:
    Type: String
    Description: Snapshot to build DB off of; leave as 'none' if new DB
    Default: none

Conditions:
  isNewDb: !Equals [!Ref SnapshotArn, none]

  isSnapshotDb:
    !Not [!Equals [!Ref SnapshotArn, none]]

Generating a password for the instance and adding a secret to Secrets Manager is done by the following portion of the template.

Credentials:
  Condition: isNewDb
  DeletionPolicy: Retain
  Type: AWS::SecretsManager::Secret
  Properties:
    GenerateSecretString:
      SecretStringTemplate: '{"username": "admin"}'
      GenerateStringKey: "password"
      PasswordLength: 16
      ExcludeCharacters: '"@/\'

This is a similar snippet to what can be found in AWS references. First, I will remind you that there are conditions in this template that determine whether brand new database instances will be launched or if those instances will be based on a snapshot. If the instances are based on a snapshot, they will use the master username and password that was specified at the time of the RDS cluster’s creation. For that reason, I am retaining the secrets created with new clusters with Condition: isNewDb and DeletionPolicy: Retain in case their stacks are deleted. If the stacks are deleted, this allows a new stack to be created that will launch instances using the old database’s snapshot and credentials.

In actuality, the password generation is simply a randomly generated string that is later referenced by RDS to use as the password. The SecretStringTemplate portion is the baseline value for the secret, and the generated string is given as the value for the key with the name password as denoted by GenerateStringKey. However, this alone does not make that generated string a password. What does make it a password, is the following snippet from a property of the AWS::RDS::DBCluster resource definition.

MasterUserPassword:
  !If
    - isNewDb
    - !Join
      - ''
      -
        - '{{resolve:secretsmanager:'
        - !Ref Credentials
        - ':SecretString:password}}'
    - !Ref AWS::NoValue

Now we are referencing the generated string to be used as the password. The condition for isNewDb exists because creating an AWS::RDS::DBCluster resource with a snapshot requires that the master credentials properties are not present. The snapshot property in my template looks like the following snippet.

SnapshotIdentifier:
  !If
    - isSnapshotDb
    - !Ref SnapshotArn
    - !Ref AWS::NoValue

This references the snapshot’s ARN if it was provided by the user. Lastly, there is an AWS::SecretsManager::SecretTargetAttachment resource which does some cool magic. This resource takes in the AWS::RDS::DBCluster and AWS::SecretsManager::Secret and adds the relevant database information to the secret like host, port, and engine.

While this template is not a full data recovery plan, it can help in a pinch by at least having everything set up to accept a snapshot and rebuild a database cluster if a stack is somehow deleted or a copy needs to be built.

Categories: ops | aws | serverless