Last updated on April 27, 2023
Introduction
When writing CloudFormation templates, it’s a security best practice to avoid hardcoding sensitive info, like client secrets, API keys, or passwords. Sharing templates with hardcoded details by mistake can put your infrastructure and data at risk. In this article, we’ll explore different secure methods to pass parameters in CloudFormation templates.
Using the NoEcho Attribute
To avoid hardcoding parameters in your template, you can define them during stack creation using the Parameters section. However, these values will still be visible in plaintext in the Console. To mask critical data, consider using the NoEcho attribute. The NoEcho attribute offers a simple yet effective way to securely pass sensitive parameters to a CloudFormation template.
When you set the NoEcho attribute to true, CloudFormation will redact the parameter value by displaying asterisks (***) in the Console or any API responses. This ensures that the value remains hidden from anyone with access to DescribeStacks* permissions.
Cons:
- CloudFormation does not mask information included in the Metadata and Outputs sections.
- NoEcho does not encrypt the parameter value or protect it from being accessed by someone with permission to access the CloudFormation stack.
Using Dynamic References
The most recommended way of passing sensitive parameters to a CloudFormation template is through Dynamic references. Instead of specifying sensitive info during stack creation, you may opt to store them first in AWS Secrets Manager or Systems Manager Parameter Store. Then you can use a dynamic reference so CloudFormation can retrieve and resolve them at runtime.
A dynamic reference is enclosed by curly brackets and starts with the ‘resolve’ keyword, followed by the service name and an associated key name for the parameter. Optionally, you may specify the particular version of the parameter as well. This is helpful when you intend to modify parameter values, such as updating API keys or rotating secrets. CloudFormation is not aware of any changes done to the actual parameter. Thus, you also need to manually update your template with the new reference key version and then perform a stack update operation.
CloudFormation supports the following reference key names:
- ssm (plaintext values stored in SSM Parameter Store)
{{resolve:ssm:
parameter-name
:version
}}- ideal for non-sensitive application configuration data such as endpoints, user
- ssm-secure (secure strings stored in SSM Parameter Store)
{{resolve:ssm:
parameter-name
:version
}}- ideal for non-sensitive application configuration data such as Oauth secrets, API keys, license codes
- secretsmanager (secret values stored in Secrets Manager.)
{{resolve:secretsmanager:secret-id:secret-string:json-key:version-stage:version-id}}
- ideal for database credentials
How to use dynamic reference?
Here’s a common pattern of how you use dynamic reference when creating CloudFormation stacks.
Let’s say you are building a stack for an application that is made up of a Lambda function and an RDS database. To protect the database credentials from being exposed, you wouldn’t store them in plain text in the template, function code, or the function’s environment variables. Instead, you can store the credentials as a secret in AWS Secrets Manager and then reference it in your template via the built-in secretsmanager reference key.
You can pass the secret name (in this example, “DBcreds”) as an environment variable to the Lambda function.
The Lambda function can then retrieve the secret value from AWS Secrets Manager at runtime using the GetSecretValue API.