When creating a new SSM Parameter resource you can create using the String and StringList Type however not SecureString.
This is currently possible with additional lambda functions within the template however it will make for easier to follow templates for both parameter creation and dynamic linking to ssm parameters.
We use SSM Parameters for variables, including sensitive data, so the ability to continue utilising these without manual creation before a stack deployment is desired.
Sample:
As part of the Console or API, we can create a new SecureString Parameter. It's expected that CloudFormation should also include this functionality.
Common use case: Creating a securestring parameter during stack creation from inputted parameters. These parameters can then be dynamically referenced throughout the stack.
Test case recommendation: Ability to create a securestring value and reference it from the same stack.
Reference Doc detailing the feature doesn't yet exist. https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-ssm-parameter.html
Management - Systems Manager
We currently get around this using 3 ways:
How do you expect to use this without disclosing the value of the parameter in the template?
Typically we鈥檇 use NoEcho on any sensitive parameters plus our deployment tooling would pass the values from it鈥檚 own secure store during stack creation which shouldn鈥檛 expose the value at any point of creation.
How do you expect to use this without disclosing the value of the parameter in the template?
Another use case is just to be able to create the parameter as a placeholder with an empty value so that it can be easily manually populated with the security sensitive details later, but it's existence can be referenced elsewhere in the stack immediately. We do this in our existing stack using a custom resource
Additionally, our Custom Resources supports generation a random value when the Parameter is created. This could be something that CloudFormation does itself, or CloudFormation could call secretsmanager get-random-password or kms generate-random behind the scenes.
Would like to be able to dynamically pull values from SSM for example with {environment} or {version} where environment for example is, test, staging, production and version is, 1, 2, 3, etc..
"{{resolve:ssm-secure:/{environment}/password:{version}}}"
Has anyone been able to use the ssm dynamic referencing with the Join or Sub intrinsic functions? I'm trying to write a file in the CloudFormation::Init metadata of an EC2 instance with a username and password for a service but it writes out the reference syntax to the file instead of resolving.
How do you expect to use this without disclosing the value of the parameter in the template?
Another use case is just to be able to create the parameter as a placeholder with an empty value so that it can be easily manually populated with the security sensitive details later, but it's existence can be referenced elsewhere in the stack immediately. We do this in our existing stack using a custom resource
This is a common ask, create a SecureString ParameterStore parameter with a dummy value and have another team update the value for various environments. Can we get support for this feature please?
Any update on this?
For us to properly utilise SSM Parameter store with CloudFormation we need to be able to create Secure parameters
Wow, such a required feature and still no support...
If you're ok with using a custom resource, then this is a great solution - https://github.com/glassechidna/ssmcfn, I'm using it for creating SecureString parameters with a dummy initial value "empty".
Deploy this CloudFormation template (per region if you're working across regions):
cfn.yml - Expand/Collapse
I commented out the support for UPDATE, so it only works upon CREATE+DELETE and does not attempt to update the value. I also updated the runtime from node4.3 to node 12.x (latest version that supports ZipFile)
# Source: https://github.com/glassechidna/ssmcfn/blob/master/cfn.yml
AWSTemplateFormatVersion: "2010-09-09"
Resources:
Lambda:
Type: AWS::Lambda::Function
Properties:
Handler: index.handler
Role: !GetAtt Role.Arn
Runtime: nodejs12.x
Timeout: 300
Code:
ZipFile: >
var response = require('cfn-response');
var aws = require('aws-sdk');
exports.handler = function(event, context) {
console.log(event);
var ssm = new aws.SSM();
var props = event.ResourceProperties;
var splitStackArn = event.StackId.split(':');
var region = splitStackArn[3];
var accountId = splitStackArn[4];
var stackName = splitStackArn[5].split("/")[1];
var paramName = props.Name || "cfn-" + stackName + "-" + event.LogicalResourceId; // TODO: add rand on end?
var paramArn = "arn:aws:ssm:" + region + ":" + accountId + ":parameter/" + paramName;
var cb = function(err, resp) {
var cfnRespData = { Arn: paramArn, Name: paramName };
if (err) {
console.log(err);
response.send(event, context, response.FAILED, cfnRespData, paramArn);
} else {
console.log(resp);
response.send(event, context, response.SUCCESS, cfnRespData, paramArn);
}
};
if (event.RequestType == "Create") {
var params = {
Name: paramName,
Type: "SecureString", // Hardcoded SecureString instead of using props.Type
Value: props.Value,
KeyId: props.KeyId,
Overwrite: false
};
if (props.Description) params.Description = props.Description;
if (props.KeyId) params.KeyId = props.KeyId;
ssm.putParameter(params, cb);
}
/*
Skipping update, we only want to create it
else if (event.RequestType == "Update") {
var params = {
Name: paramName,
Type: props.Type,
Value: props.Value,
KeyId: props.KeyId,
Overwrite: true
};
if (props.Description) params.Description = props.Description;
if (props.KeyId) params.KeyId = props.KeyId;
ssm.putParameter(params, cb);
}
*/
else if (event.RequestType == "Delete") {
ssm.deleteParameter({ Name: paramName }, cb);
}
};
Role:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Principal:
Service:
- lambda.amazonaws.com
Action:
- sts:AssumeRole
Path: "/"
Policies:
- PolicyName: root
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Action:
- logs:CreateLogGroup
- logs:CreateLogStream
- logs:PutLogEvents
Resource: arn:aws:logs:*:*:*
- Effect: Allow
Action:
- ssm:PutParameter
- ssm:DeleteParameter
- kms:Encrypt
Resource: "*"
Outputs:
Lambda:
Description: Cfn polyfill for SSM parameter store
Value: !GetAtt Lambda.Arn
Export:
Name: CfnParamStore
HelperRole:
Description: IAM Role
Value: !GetAtt Role.Arn
Export:
Name: SSMHelperRole
example.yaml - Expand/Collapse
I added the Name property and some comments. Also, there's no need to define the Type since the default type in the Lambda is SecureString.
AWSTemplateFormatVersion: "2010-09-09"
Resources:
SecureParam:
Type: Custom::CfnParamStore
Properties:
Name: /my-app/development/my-secret
ServiceToken: !ImportValue CfnParamStore # Required to use the Helper
Value: empty # Initial dummy value
Outputs:
ParamArn:
Description: Arn of param in SSM param store
Value: !GetAtt SecureParam.Arn
Any update on this? Our organization has use cases as described above. Our workflow is like what @jospas describes. For new deployments which require secrets we deploy the app with a dummy value REPLACE_WITH_SECRET_STRING and as part of our deployment process manually replace that value with the actual secret content. Having to change the parameter type introduces undesirable configuration drift and another step in our deployment process.
Any fear of invalid/insecure usage has already been accepted because AWS::SecretsManager::Secret already has a SecretString field.
It would be nice to get this prioritized.
Most helpful comment
This is a common ask, create a SecureString ParameterStore parameter with a dummy value and have another team update the value for various environments. Can we get support for this feature please?