How To Use CloudFormation Macros to Securely Provide Database Credentials

Wondering how AWS’ release of CloudFormation Macros will work for your AWS account and passwords? Joe Keegan, BlueChipTek Lead Cloud Services Architect, explains how two of AWS’ newest features work in conjunction to improve the security of your AWS account.

AWS has released two new features recently and I wanted to see how I could use them together to securely handle passwords in CloudFormation templates. These features are CloudFormation Macros and CloudFormation Supporting AWS Systems Manager Secure String Parameters.

I want to briefly cover what each feature does and then show they can be used together to securely handle passwords in CloudFormation templates.

AWS-CloudFormation.png#asset:3236

CloudFormation Macros

CloudFormation Macros can be used to generate parts or transform an entire CloudFormation template. This is the same technology driving the AWS::Serverless transforms used as part of SAM. At the heart of CloudFormation Macros are Lambdas which receive requests to generate JSON that can be used as part of the CloudFormation template or as the entire template.

To me there could be a little overlap between CloudFormation Macros and Lambda backed CloudFormation Custom Resources, but CloudFormation Macros are designing to generate and integrate into CloudFormation Templates. This make the job of generating parts of a CloudFormation easier with the Macros. Macros offer some flexibility and capabilities that Custom Resources do not, such as being able to transform an entire template, like you see with SAM. So, a good addition to your CloudFormation tool kit.

Secure String Parameters as Dynamic Refences

I’ve done this a million times. You go to deploy a CloudFormation stack and need to provide a password or other secret that is used to configure the resources you are deploying. In my experience this is most commonly needed when deploying RDS Databases. So, you need to either create some password and stick it in a password vault or go to the password vault and get the password to provide as a parameter.

AWS Systems Manager Parameter Store has Secure Strings which can be used as your password vault, and now CloudFormation supports references to these Secure Strings. So, when you deploy a stack you can provide the name of the Secure String in the Parameter Store and that secret can be dynamically provided to the resource created by CloudFormation. Implementation-wise CloudFormation does not return the actual parameter value for Secure Strings in any API calls, but rather returns the literal dynamic reference. This means that you are limited to what resources and properties you can use the Dynamic Reference with. A list of support resources or properties can be found here.

Putting It Together

There are some challenges with using Secure String Dynamic References in which I have used a CloudFormation Macro to overcome. The first challenge is that you still need to go create a Secure String before you use it as a dynamic reference, and the second challenge is that you must specify a version of the Secure String you wish you use (i.e. version 1, 2, 3, etc). There is no “use the latest version” option, which may or may not be fine for your use case.

The GenerateSecret Macro deals with these two issues. The Macro is provided a Secure String name and if that Secure String exists then it returns a dynamic reference to the latest version. If the Secure String doesn’t exist, the Macro will create one and return a dynamic reference to the Secure String it just created. This can simplify deployment of RDS databases and other resources along with making sure the password never ends up in Clear Text anywhere.

An example of using this macro for an RDS database test is as follows:

  DB:
    Type: AWS::RDS::DBInstance
    Properties:
      AllocatedStorage: 20
      DBInstanceClass: db.t2.small
      Engine: MySQL
      MasterUsername: admin
      MasterUserPassword:
        Fn::Transform:
         - Name: GenerateSecret
           Parameters:
             SecretName: /Dev/Db/AdminPassword
             Exclude:
              - '"'
              - '/'
              - '@'

You must provide the SecureName parameter, but other parameters such as the length of the password, what character types to include and specific characters to exclude can also be provided.

You can check out the GenerateSecret Macro in our aws-tools repo.