AWS Logo
Menu
SSM Parameters in AWS CDK

SSM Parameters in AWS CDK

This article explains some common use cases using aws-ssm in AWS CDK

Pahud Hsieh
Amazon Employee
Published Sep 10, 2024
Last Modified Sep 13, 2024
AWS Systems Manager Parameter Store (SSM) integration in AWS CDK(known as the aws-ssm module) is a powerful feature that enables developers to create and reference SSM Parameters easily. This functionality has become one of the most widely used CDK constructs. In this article, we will explore several common use cases for aws-ssm in AWS CDK, demonstrating how it can enhance your infrastructure-as-code projects.

What are the SSM Parameters

AWS Systems Manager Parameter Store, commonly known as SSM Parameters, is a powerful feature within AWS Systems Manager. It provides a secure and hierarchical storage solution for managing configuration data and secrets. This centralized store allows you to efficiently handle various types of information, from plaintext data like database connection strings to sensitive information such as passwords.
Key features of SSM Parameters include:
  1. Secure storage: Offers encryption for sensitive data.
  2. Hierarchical organization: Allows logical grouping of parameters.
  3. Version control: Maintains a history of parameter changes.
  4. Integration: Seamlessly works with other AWS services and applications.
  5. Access control: Utilizes IAM for fine-grained access management.
By leveraging SSM Parameters, you can streamline your configuration management process and enhance the security of your AWS environment.

Create an SSM Parameter with CDK

To create an SSM Parameter, from CloudFormation's perspective, it's as simple as create an resource.
Creating an SSM Parameter in AWS CloudFormation is straightforward. You can accomplish this by defining an AWS::SSM::Parameter resource in your CloudFormation template. This resource type allows you to easily create and manage SSM Parameters as part of your infrastructure-as-code approach. In CDK, use ssm.StringParameter():
This creates an SSM Parameter resource named "FooParameter" with a value of "Foo".
Get the value with AWS CLI:

Reference or Lookup an SSM Parameter with CDK

CDK doesn't directly access SSM parameter values during synthesis. Instead, it generates CloudFormation templates that reference these parameters, allowing for dynamic value resolution at deployment time.
CloudFormation offers two methods to reference SSM parameters:
  • Dynamic References
  • Template Parameters
CDK allows you to lookup ssm parameter:
Now, let's dive into each of them.
  • Dynamic References
Dynamic References can be represented in the following formats:
- {{resolve:ssm:reference-key:version}} - SSM plaintext parameter.
- {{resolve:ssm-secure:reference-key:version}} - SSM secure string parameter.
- {{resolve:secretsmanager:secret-id:SecretString:json-key:version-stage:version-id}} - Secrets Manager secret.
Read document for more details.
Let's say if we have built our own EC2 AMI and stored in the SSM Parameter as golden-ami-id. How to create an EC2 instance using that AMI ID?
In CDK:
synth Outputs:
When cdk deploy, CloudFormation resolves the value at deployment time using its execution service role. The actual value remain unexposed in the CloudFormation template. Ensure the CloudFormation execution role has permission to read these parameters.
Dynamic References support secrets in AWS Secrets Manager as well:
CDK:
cdk synth
Check this document for more details.
Please note - Dynamic References in CloudFormation are represented as {{...}} in the templates that would only be resolved at template deployment time. Though the value would not be exposed in the template, when the target resource is created, the value could still be exposed in the console of the target resource. Let's take lambda.Function for example:
On cdk synth, the CFN template is having:
But if you check the provisioned Function from AWS Lambda console, you would see that SECRET value exposed as plain-text in its environment variables configuration. The best option for AWS Lambda at this moment is using a specific Lambda extension that retrieves the value from the extension so we just pass the parameter names and versions in the Lambda environment variables. See this doc for more details.
For me, if the parameter value is sensitive:
1. Make sure it's stored as SecureString in the SSM parameter store or Secrets Manager with KMS key encrypted
2. Only pass the names of the parameters in CDK/CloudFormation.
3. It's the target resource's responsibility to get it resolved from the provided names using granted permissions to get the parameter values decrypted using relevant keys.
  • Template Parameters
Alternatively, you can use CloudFormation template parameters and reference them in resource properties. Consider this CDK example that creates a SecurityGroup with its description sourced from an SSM Parameter:
On synth:
CDK creates CfnParameter resources like this for you with AWS::SSM::Parameter::Value<String> type and then be referenced from the resources.
On cdk deploy, CloudFormation retrieves the SSM parameter values as the default for the template parameters, which are then used in the security group description.
Please note - the parameter values would be exposed in the CloudFormation console Parameters tab. You can get the values using AWS CLI as well:
Now, what about fromSecureStringParameterAttributes()?
cdk synth
fromSecureStringParameterAttributes() creates a dynamic reference with ssm-secure for the value stored as a SecureString.
while fromStringParameterAttributes() creates a template reference with the risk of exposure as mentioned above.
Lookup via SDK calls in AWS CDK
This looks up the value using AWS JS SDK in CDK CLI before synthesizing the template:
On cdk synth, the CDK CLI:
a. checks for cached values in cdk.context.json
b. if found, uses the cached value as a plain-text string in the CloudFormation template.
c. If not found, creates a placeholder, replaces it with the actual value, and caches it in cdk.context.json
Please note:
  1. The value would be plain-text exposed in the CloudFormation template. Not recommended for secure strings or credentials.
  2. The IAM principal that executes CDK CLI requires the permission to get the value of the SSM parameters.
  3. If you have a conditional check on the value, make sure you check if its the placeholder value. CDK synthesizes multiple times until all resolvable values are resolved. Let's take this for example. Assuming vpcId is a pre-defined ssm parameter with string value like vpc-11111 and we are conditionally assigning a value to environmentName based on the vpcId retrieved from parameter store:
When running cdk synth, a dummy value dummy-value-for-VpcId is generated as a placeholder if the VpcId is not found in the local context cache (cdk.context.json). This placeholder injection allows CDK to maintain the template structure while indicating that a real value needs to be resolved.
To deal with that:
Now, cdk synth | more again. You will see the first synth is gracefully handled for the dummy value and the 2nd synth would be handled correctly.
If you check cdk.context.json
Find out the context number with cdk conetxt and reset it using
Read Lookup existing parameters for more details.

Cross-Account or Cross-Region support

You need cross-account or/and cross-region SSM parameter retrieval or reference support and wondering if CDK has that?
Well, first, we need to know that CloudFormation basically does not support cross-region or cross-account parameter or resource reference except for some very few cases that the services can have that support for example AWS::RDS::GlobalCluster support its source DB in a different region by specifying SourceDBClusterIdentifier. Technically it's not CloudFront that reference resources cross-region but the RDS handling the cross-region for you.
cross-account + same region
Now, in AWS CDK, if we need a Consumer Account A referencing an SSM Parameter from the Producer Account B in the same region. We need cross-account SSM parameter sharing using fromStringParameterArn() method. Bear in mind this method requires:
  1. The parameter ARN is from a different account sharing its SSM parameter to the consuming account.
  2. The parameter has to be in the advanced tier.
  3. The sharing account has to explicitly create an AWS RAM ResourceShare with the consuming account and the consuming account must accept the invitation before it's allowed to reference that cross-account.
  4. See cross-account SSM Parameters sharing in aws-ssm README for more details.
cross-region (regardless same or different account)
CloudFormation does not support cross-region SSM parameter reference out-of-the box so CDK would not have that either. You can implement 2 different solutions to address that:
a) Using custom resource in which a Lambda function could retrieve the remote parameters for you.
b) Before CDK deploy, using a wrapper script to "fetch" remote parameters and create the same copies in current region before the cdk deploy. This allows your CDK code to simply reference local SSM parameters which serve as a snapshot of the remote ones. You need to design your deployment pipeline that runs the one-time replication before the cdk deploy.

Common Questions

  1. Does CDK support cross-region SSM parameters retrieval or reference just like Amazon ECS does?
    ANS: From CloudFormation's perspective, it does not support cross-region parameter reference. The reason ECS supports cross-regional secrets is because there's an ECS task execution role that brings up the tasks and containers and it would retrieve secrets for you with cross-region capabilities. With that being said, it's ECS task execution role that retrieves the secrets/parameters, not the CloudFormation service role.
  2. As ssm-secure only support a few limited services per described in this doc, how to protect my sensitive property so I can pass it to a target service without exposing that in my IaC code? For example, the ProviderDetails prop of AWS::Cognito::UserPoolIdentityProvider is asking private_key and obviously Cognito is not supported by ssm-secure, what should I do?
    ANS: We need to create a Dynamic Reference with secretsmanager support as this string:
    "{{resolve:secretsmanager:SECRET_NAME:SecretString:JSON_FIELD::}}" and pass it to the relevant prop. In CDK we can build that with:
Now, always validate with cdk synth:
We got the Dynamic Reference in the private_key property now. This ensures your private_key is stored in AWS Secrets Manager with KMS protected.
  1. I already knows the ARN of a parameter. Can I pass it to fromStringParameterAttributes() or fromStringParameterName()?
    ANS: If you look at its implementation of the fromStringParameterAttributes method, it essentially creates a dynamic reference or CfnParameter in some cases. All it requires is the parameter name and optionally the version. If you already have the ARN of the parameter, you can tell the parameter name and version from the ARN string and simply pass them as options to fromStringParameterAttributes().
  2. But there is fromStringParameterArn()?
    ANS: That's used for cross-account sharing only. See README for more details.

Key Take-Aways

  1. When handling credentials in AWS environments, it's best practice to only pass parameter names to resources such as Lambda Functions and ECS services, rather than passing the actual credential values. The responsibility for retrieving and decrypting these values should lie with the target resource itself, using the permissions it has been granted. Infrastructure as Code (IaC) tools, including AWS CDK and CloudFormation, should not have any direct access to the credential values. This approach enhances security by minimizing exposure of sensitive information and adhering to the principle of least privilege.
  2. If the target service can't access the credentials from your provided parameter names or secret names, you need Dynamic References. Bear in mind that ssm-secure only supports a few services. In many cases you would need to use Dynamic References with secretsmanager support instead. Check "Common Questions" no.2 above for a real-life use case. Always read Considerations when using dynamic references.
  3. Avoid template parameters for secrets as they may expose sensitive information.
  4. valueFromLookup() invokes SDK calls before template synthesis. Be mindful of placeholder values and cdk.context.json cache management. Use 'cdk context --reset' to clear cached values.
  5. For cross-account parameter sharing, use fromStringParameterArn(). Consult the doc for examples.
  6. Always cdk synth and check the synthesized outputs and verify if any dynamic reference or template parameter is created. Understand what's happening under the hood.
  7. Cross-account sharing with conditions using fromStringParameterArn().
  8. Cross-region sharing is not supported out-of-the-box. Consider two workarounds mentioned above.


Further Reading

  • ContextProvider dummy value handling tips with elbv2.NetworkLoadBalancer.fromLookup() #30828
  • Use Dynamic Reference with secretsmanager to protect the private_key prop in UserPoolIdentityProviderApple - #31378
     

Any opinions in this post are those of the individual author and may not reflect the opinions of AWS.

Comments