2
votes

I have a nested CloudFormation stack template that describes database and it's related resources. I need to create multiple databases for various environments (e.g. stage, qa, prod).

I store database password for each environment in SSM parameter store as secure strings, e.g.:

  • /acme/stage/DB_PASSWORD
  • /acme/qa/DB_PASSWORD
  • /acme/prod/DB_PASSWORD

I've tried to resolve the SSM parameter in parent template and pass it to a nested stack, but it looks like it's not working.

# parent.template

StageDatabaseStack:
  Type: AWS::CloudFormation::Stack
  Properties:
    TemplateURL: "…/database.template"
    Parameters:
      DatabasePassword: '{{resolve:ssm-secure:/acme/stage/DB_PASSWORD:1}}'

QaDatabaseStack:
  Type: AWS::CloudFormation::Stack
  Properties:
    TemplateURL: "…/database.template"
    Parameters:
      DatabasePassword: '{{resolve:ssm-secure:/acme/qa/DB_PASSWORD:1}}'

ProdDatabaseStack:
  Type: AWS::CloudFormation::Stack
  Properties:
    TemplateURL: "…/database.template"
    Parameters:
      DatabasePassword: '{{resolve:ssm-secure:/acme/prod/DB_PASSWORD:1}}'


# database.template

AWSTemplateFormatVersion: "2010-09-09"

Parameters:
  DatabasePassword:
    Type: String

Resources:
  Database:
    Type: AWS::RDS::DBInstance
    Properties:
      MasterUserPassword: !Ref DatabasePassword

This approach gives me an error:

An error occurred (ValidationError) when calling the CreateStack operation: SSM Secure reference is not supported in: [AWS::CloudFormation::Stack/Properties/Parameters/DatabasePassword]


According to the docs, it is also possible to define password parameter as a special AWS::SSM::Parameter::Value<String> type, but it is stated, that:

AWS CloudFormation does not support defining template parameters as SecureString Systems Manager parameter types.

I've actually tried to use it, but it gives me an error:

Parameters [/acme/stage/DB_PASSWORD] referenced by template have types not supported by CloudFormation.


SO, how do I actually pass secure database password to a database resource in this scenario? I want to use a single CloudFormation template to manage all three databases and their related resources.

2

2 Answers

2
votes

Try:

'{{resolve:ssm-secure:/acme/stage/DB_PASSWORD:1}}'

I.e. with apostrophs. See YAML examples in the documentation:

  MyS3Bucket:
    Type: 'AWS::S3::Bucket'
    Properties:
      AccessControl: '{{resolve:ssm:S3AccessControl:2}}' 
1
votes

The only way to pass a secure SSM parameter to a nested stack I've found is to pass it as a string, instead of trying to use more sensible AWS::SSM::Parameter::Value<String> and then resolving the secure value in a nested stack by building the dynamic reference with !Sub function.

Here's a working example:

# parent.template

StageDatabaseStack:
  Type: AWS::CloudFormation::Stack
  Properties:
    Parameters:
      DatabasePasswordSsmKey: "/acme/stage/DB_PASSWORD:1"

ProdDatabaseStack:
  Type: AWS::CloudFormation::Stack
  Properties:
    Parameters:
      DatabasePasswordSsmKey: "/acme/prod/DB_PASSWORD:1"
# database.template

Parameters:
  DatabasePasswordSsmKey:
    Description: SSM property key for database password
    Type: String

Database:
  Type: AWS::RDS::DBInstance
  Properties:
    MasterUserPassword: !Sub "{{resolve:ssm-secure:${DatabasePasswordSsmKey}}}"