You can reference SSM parameters and secrets in CloudFormation without hardcoding values by using the {{resolve:ssm:parameter-name}} and {{resolve:secretsmanager:secret-name}} dynamic references.
Let’s see this in action. Imagine you have a database password stored in AWS Secrets Manager and an environment name stored as an SSM Parameter. You want to use these in a CloudFormation template to configure an EC2 instance.
Here’s a snippet of a CloudFormation template demonstrating this:
Resources:
MyEC2Instance:
Type: AWS::EC2::Instance
Properties:
ImageId: ami-0abcdef1234567890
InstanceType: t2.micro
UserData:
Fn::Base64: !Sub |
#!/bin/bash
echo "Setting up environment: ${EnvironmentName}"
echo "Connecting to database with password: ${DbPassword}"
Metadata:
EnvironmentName: "{{resolve:ssm:my-app/environment}}"
DbPassword: "{{resolve:secretsmanager:my-app/db/credentials:username}}" # Example for username, can also get password directly
Outputs:
InstanceId:
Description: The Instance ID of the created EC2 instance
Value: !Ref MyEC2Instance
In this example:
-
{{resolve:ssm:my-app/environment}}tells CloudFormation to fetch the value of the SSM parameter namedmy-app/environment. -
{{resolve:secretsmanager:my-app/db/credentials:username}}tells CloudFormation to fetch theusernamefrom the secret namedmy-app/db/credentialsin Secrets Manager. You can also fetch the entire secret as a JSON string or a specific key likepassword.
When CloudFormation processes this template, it resolves these dynamic references before creating the resources. The UserData script will then receive the actual environment name and database password, not the placeholder strings.
The core problem this solves is avoiding sensitive information like passwords or connection strings directly in your CloudFormation templates, which are often stored in version control. It also allows for environment-specific configurations without creating entirely separate templates. You can have one template and use different SSM parameters or secrets for different deployment environments (dev, staging, prod).
Internally, CloudFormation’s change set generation and deployment process includes a step to resolve these dynamic references. It makes API calls to SSM Parameter Store and Secrets Manager to retrieve the values. If the parameter or secret doesn’t exist, or if the IAM role executing the CloudFormation stack doesn’t have permissions to read them, the stack creation or update will fail.
The Fn::Sub intrinsic function is used here to embed the resolved values into the UserData script. CloudFormation substitutes the resolved values into the ${EnvironmentName} and ${DbPassword} placeholders.
It’s crucial to ensure the IAM role CloudFormation uses has the necessary ssm:GetParameter and secretsmanager:GetSecretValue permissions for the parameters and secrets you are referencing.
You can also reference parameters that require decryption using KMS. For example, {{resolve:ssm:my-secure-parameter:SecureString}} will ensure that if the parameter is a SecureString type, it’s decrypted using the associated KMS key.
The most surprising thing is how seamlessly CloudFormation handles these lookups before any resource provisioning begins. It’s not an in-band operation where the resource itself fetches the value; CloudFormation acts as an orchestrator, gathering all necessary external data first.
The next concept you’ll likely encounter is managing the lifecycle of these parameters and secrets, including rotation and access control, especially as your infrastructure scales.