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.
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:
- Secure storage: Offers encryption for sensitive data.
- Hierarchical organization: Allows logical grouping of parameters.
- Version control: Maintains a history of parameter changes.
- Integration: Seamlessly works with other AWS services and applications.
- 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.
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:
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:
- Lookup via ContextProvider with valueFromLookup()
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.
- {{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.
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
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
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:
- The value would be plain-text exposed in the CloudFormation template. Not recommended for secure strings or credentials.
- The IAM principal that executes CDK CLI requires the permission to get the value of the SSM parameters.
- 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 likevpc-11111
and we are conditionally assigning a value toenvironmentName
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 usingRead Lookup existing parameters for more details.
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:- The parameter ARN is from a different account sharing its SSM parameter to the consuming account.
- The parameter has to be in the advanced tier.
- 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.
- 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
.- 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. - 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.- 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(). - But there is fromStringParameterArn()?
ANS: That's used for cross-account sharing only. See README for more details.
- 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.
- 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. - Avoid template parameters for secrets as they may expose sensitive information.
valueFromLookup()
invokes SDK calls before template synthesis. Be mindful of placeholder values andcdk.context.json
cache management. Use 'cdk context --reset' to clear cached values.- For cross-account parameter sharing, use
fromStringParameterArn()
. Consult the doc for examples. - 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. - Cross-account sharing with conditions using
fromStringParameterArn()
. - Cross-region sharing is not supported out-of-the-box. Consider two workarounds mentioned above.
Any opinions in this post are those of the individual author and may not reflect the opinions of AWS.