16

Stuck with the usage of SecureString from AWS Parameter Store. I am trying to refer to the database password as:

DatabasePassword:
    Type: AWS::SSM::Parameter::Value<SecureString>
    NoEcho: 'true'
    Default: /environment/default/database_password
    Description: The database admin account password

This throws an error:

An error occurred (ValidationError) when calling the CreateStack operation: Template format error: Unrecognized parameter type: SecureString

However, if I refer to this parameter as String instead of SecureString it throws a different error:

An error occurred (ValidationError) when calling the CreateStack operation: Parameters [/environment/default/database_password] referenced by template have types not supported by CloudFormation.

I did try using '{{resolve:ssm-secure:parameter-name:version}}' and it works for database configuration:

MasterUsername: !Ref DatabaseUsername
MasterUserPassword: '{{resolve:ssm-secure:/environment/default/database_password:1}}'

However, I'm using AWS Fargate docker containers where I'm supplying these values as Environment variables:

Environment:
  - Name: DATABASE_HOSTNAME
    Value: !Ref DatabaseHostname
  - Name: DATABASE_USERNAME
     Value: !Ref DatabaseUsername
  - Name: DATABASE_PASSWORD
    Value: '{{resolve:ssm-secure:/environment/default/database_password:1}}'

This throws an error:

An error occurred (ValidationError) when calling the CreateStack operation: SSM Secure reference is not supported in: [AWS::ECS::TaskDefinition/Properties/ContainerDefinitions/Environment]

Unable to use secure strings in my implementation. Is there any workaround to this problem? AWS announced support for SecureString last year, but unable to find the documentation. All I found was to use resolve which only works in some cases.

References:

1

2

1

4 Answers 4

16

CloudFormation does not support SecureString as template parameter type. You can confirm it in the documentation below, let me quote it.

In addition, AWS CloudFormation does not support defining template parameters as SecureString Systems Manager parameter types. However, you can specify Secure Strings as parameter values for certain resources by using dynamic parameter patterns.

Reference: https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/parameters-section-structure.html#aws-ssm-parameter-types

As you mention you "could" solve it using dynamic parameter patterns, but only a limited amount of resources supports it. ECS and Fargate does not.

Reference: https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/dynamic-references.html

Maybe you can address it using Secrets Manager, instead you set the password as environment variable for you container, your application get the password in runtime from Secrets Manager, this also improves your security, the password will not be in clear text inside the container.

Below you can see one example of this solution, it is not for container, but the "way of work" is the same using environment variable and Secrets Manager.

Reference: https://aws.amazon.com/blogs/security/how-to-securely-provide-database-credentials-to-lambda-functions-by-using-aws-secrets-manager/

2
  • 1
    I did consider this option of using Secrets Manager. If I make changes in my application to refer these variables from Secrets Manager then it will work if I pull the credentials directly from Secrets Manager inside docker container. However, for my local development, I do not want to have a dependency on AWS where I'm referring to my local instance of database instead of AWS RDS instance.
    – kk.
    Commented Jul 5, 2019 at 9:45
  • 24
    Why on the earth AWS released this capability for only String and not SecureString? What's the problem in supporting this? They are making it very difficult for development using CloudFormation. :-(
    – kk.
    Commented Jul 5, 2019 at 9:47
3

In my case, I got the same underlying error with the AWS CDK:

... Error [ValidationError]: Parameters [/My/Secret/Parameter] referenced by template have types not supported by CloudFormation.

The error was fixed by changing the method name fromStringParameterAttributes to fromSecureStringParameterAttributes:

const stringValue = aws_ssm.StringParameter.fromStringParameterAttributes(this, 'MyValue', {
  parameterName: '/My/Secret/Parameter',
  // 'version' can be specified but is optional.
}).stringValue;

To:

const stringValue = aws_ssm.StringParameter.fromSecureStringParameterAttributes(this, 'MyValue', {
  parameterName: '/My/Secret/Parameter',
  // 'version' can be specified but is optional.
}).stringValue;

P.S. This most likely has changed since 2019 when the question was originally asked (sources welcome). That stated, I'm using the AWS CDK which synthesizes CloudFormation templates, so it must now be possible.

1

The AWS Secrets Manager can be used to obtain secrets for CloudFormation templates, even where they are not things such as database passwords.

Here is a link to the documentation: https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/dynamic-references.html#dynamic-references-secretsmanager

There are 3 parts to a Secrets Manager secret:

  • The Secret's Name e.g. PROD_DB_PASSWORD
  • The Secret's Key e.g. DB_PASSWORD
  • And the actual Secret Value

You would then resolve the above secret in your CloudFormation template using:

'{{resolve:secretsmanager:PROD_DB_PASSWORD:SecretString:DB_PASSWORD}}'
0
1

I know this post is quite old, but I came across a situation where I needed to use a SecureString and found both this post and a blog post that describes a workaround. I thought this could help some people.

Original Post Here

Basically, you can create a .conf file in your .ebextensions folder like this:

---
packages:
  yum:
    bash: []
    curl: []
    jq: []
    perl: []
files:
  /opt/elasticbeanstalk/hooks/restartappserver/pre/00_resolve_ssm_environment_variables.sh:
    mode: "000700"
    owner: root
    group: root
    content: |
      #!/usr/bin/env bash
      /usr/local/bin/resolve_ssm_environment_variables.sh
 
  /opt/elasticbeanstalk/hooks/appdeploy/pre/00_resolve_ssm_environment_variables.sh:
    mode: "000700"
    owner: root
    group: root
    content: |
      #!/usr/bin/env bash
      /usr/local/bin/resolve_ssm_environment_variables.sh
 
  /opt/elasticbeanstalk/hooks/configdeploy/pre/00_resolve_ssm_environment_variables.sh:
    mode: "000700"
    owner: root
    group: root
    content: |
      #!/usr/bin/env bash
      /usr/local/bin/resolve_ssm_environment_variables.sh
 
  /usr/local/bin/resolve_ssm_environment_variables.sh:
    mode: "000700"
    owner: root
    group: root
    content: |
      #!/usr/bin/env bash
      set -Eeuo pipefail
 
      # Resolve SSM parameter references in the elasticbeanstalk option_settings environment variables.
      # SSM parameter references must take the same form used in CloudFormation, see https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/dynamic-references.html#dynamic-references-ssm-secure-strings
      # supported forms are:
      # {{resolve:ssm-secure-env:path:version}}
      # {{resolve:ssm-secure-env:path}}
      # {{resolve:ssm-env:path:version}}
      # {{resolve:ssm-env:path}}
      # where "path" is the SSM parameter path and "version" is the parameter version.
 
      if [[ -z "${AWS_DEFAULT_REGION:-}" ]]; then
        # not set so get from configuration
        AWS_DEFAULT_REGION="$(aws configure get region)" || :
      fi
      if [[ -z "${AWS_DEFAULT_REGION:-}" ]]; then
        # not set so get from metadata
        AWS_DEFAULT_REGION="$(curl -s http://169.254.169.254/latest/dynamic/instance-identity/document | jq -r .region)" || :
      fi
      if [[ -z "${AWS_DEFAULT_REGION:-}" ]]; then
        echo "Could not determine region." 1>&2
        exit 1
      fi
      export AWS_DEFAULT_REGION
 
      readonly CONTAINER_CONFIG_FILE="${1:-/opt/elasticbeanstalk/deploy/configuration/containerconfiguration}"
      readonly TEMP_CONTAINER_CONFIG_FILE="$(mktemp)"
 
      i=0
      for envvar in $(jq -r ".optionsettings[\"aws:elasticbeanstalk:application:environment\"][]" "${CONTAINER_CONFIG_FILE}"); do
        envvar="$(echo "${envvar}" | perl -p \
          -e 's|{{resolve:ssm(?:-secure)-env:([a-zA-Z0-9_.-/]+?):(\d+?)}}|qx(aws ssm get-parameter-history --name "$1" --with-decryption --query Parameters[?Version==\\\x60$2\\\x60].Value --output text) or die("Failed to get SSM parameter named \"$1\" with version \"$2\"")|eg;' \
          -e 's|{{resolve:ssm(?:-secure)-env:([a-zA-Z0-9_.-/]+?)}}|qx(aws ssm get-parameter --name "$1" --with-decryption --query Parameter.Value --output text) or die("Failed to get SSM parameter named \"$1\"")|eg;')"
        export envvar
        jq ".optionsettings[\"aws:elasticbeanstalk:application:environment\"][${i}]=env.envvar" < "${CONTAINER_CONFIG_FILE}" > "${TEMP_CONTAINER_CONFIG_FILE}"
        cp "${TEMP_CONTAINER_CONFIG_FILE}" "${CONTAINER_CONFIG_FILE}"
        rm "${TEMP_CONTAINER_CONFIG_FILE}"
        ((i++)) || :
      done

And then you can use it like that in CloudFormation template (or really any way you want, I use it with Terraform). Note that there is an extra -env suffix to distinguish from the native resolver.

---
AWSTemplateFormatVersion: '2010-09-09'
Resoures:
  BeanstalkEnvironment:
    Type: AWS::ElasticBeanstalk::Environment
    Properties:
      OptionSettings:
        -
          Namespace: "aws:elasticbeanstalk:application:environment"
          OptionName: SPRING_DATASOURCE_PASSWORD
          Value: !Sub "{{resolve:ssm-secure-env:/my/parameter:42}

Not the answer you're looking for? Browse other questions tagged or ask your own question.