Reputation: 3945
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:
Upvotes: 17
Views: 25791
Reputation: 1907
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.
Upvotes: 4
Reputation: 3334
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.
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}
Upvotes: 1
Reputation: 1873
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:
You would then resolve the above secret in your CloudFormation template using:
'{{resolve:secretsmanager:PROD_DB_PASSWORD:SecretString:DB_PASSWORD}}'
Upvotes: 1
Reputation: 4476
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.
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
.
Upvotes: 17