Skip to content

Commit b33b9b0

Browse files
authored
fix(ssm): StringParameter.fromSecureStringParameterAttributes not working without version (#22618)
It is possible to omit the `version` of an SSM SecureString parameter. When omitted, the reference generated by CDK results in a ValidationError when applying the changes. e.g. ``` Error [ValidationError]: Incorrect format is used in the following SSM reference: [{{resolve:ssm-secure:/some/parameter:}}] ``` Related to #18729 Replaces #22311 ---- ### All Submissions: * [x] Have you followed the guidelines in our [Contributing guide?](https://github.com/aws/aws-cdk/blob/main/CONTRIBUTING.md) ### Adding new Unconventional Dependencies: * [ ] This PR adds new unconventional dependencies following the process described [here](https://github.com/aws/aws-cdk/blob/main/CONTRIBUTING.md/#adding-new-unconventional-dependencies) ### New Features * [x] Have you added the new feature to an [integration test](https://github.com/aws/aws-cdk/blob/main/INTEGRATION_TESTS.md)? * [x] Did you use `yarn integ` to deploy the infrastructure and generate the snapshot (i.e. `yarn integ` without `--dry-run`)? *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license*
1 parent 2fcc701 commit b33b9b0

File tree

21 files changed

+1823
-295
lines changed

21 files changed

+1823
-295
lines changed

packages/@aws-cdk/aws-ssm/README.md

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,31 @@ This module is part of the [AWS Cloud Development Kit](https://github.com/aws/aw
1818
You can reference existing SSM Parameter Store values that you want to use in
1919
your CDK app by using `ssm.StringParameter.fromStringParameterAttributes`:
2020

21-
[using SSM parameter](test/integ.parameter-store-string.lit.ts)
21+
```ts
22+
// Retrieve the latest value of the non-secret parameter
23+
// with name "/My/String/Parameter".
24+
const stringValue = ssm.StringParameter.fromStringParameterAttributes(this, 'MyValue', {
25+
parameterName: '/My/Public/Parameter',
26+
// 'version' can be specified but is optional.
27+
}).stringValue;
28+
const stringValueVersionFromToken = ssm.StringParameter.fromStringParameterAttributes(this, 'MyValueVersionFromToken', {
29+
parameterName: '/My/Public/Parameter',
30+
// parameter version from token
31+
version: parameterVersion,
32+
}).stringValue;
33+
34+
// Retrieve a specific version of the secret (SecureString) parameter.
35+
// 'version' is always required.
36+
const secretValue = ssm.StringParameter.fromSecureStringParameterAttributes(this, 'MySecureValue', {
37+
parameterName: '/My/Secret/Parameter',
38+
version: 5,
39+
});
40+
const secretValueVersionFromToken = ssm.StringParameter.fromSecureStringParameterAttributes(this, 'MySecureValueVersionFromToken', {
41+
parameterName: '/My/Secret/Parameter',
42+
// parameter version from token
43+
version: parameterVersion,
44+
});
45+
```
2246

2347
You can also reference an existing SSM Parameter Store value that matches an
2448
[AWS specific parameter type](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/parameters-section-structure.html#aws-specific-parameter-types):

packages/@aws-cdk/aws-ssm/lib/parameter.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -492,7 +492,10 @@ export class StringParameter extends ParameterBase implements IStringParameter {
492492
*/
493493
public static fromSecureStringParameterAttributes(scope: Construct, id: string, attrs: SecureStringParameterAttributes): IStringParameter {
494494
const version = attrs.version ? Tokenization.stringifyNumber(attrs.version) : '';
495-
const stringValue = new CfnDynamicReference(CfnDynamicReferenceService.SSM_SECURE, `${attrs.parameterName}:${version}`).toString();
495+
const stringValue = new CfnDynamicReference(
496+
CfnDynamicReferenceService.SSM_SECURE,
497+
version ? `${attrs.parameterName}:${version}` : attrs.parameterName,
498+
).toString();
496499

497500
class Import extends ParameterBase {
498501
public readonly parameterName = attrs.parameterName;
Lines changed: 48 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,26 @@
1-
/// !cdk-integ *
21
import * as cdk from '@aws-cdk/core';
2+
import * as integ from '@aws-cdk/integ-tests';
33
import * as ssm from '../lib';
44

5+
const SECURE_PARAM_NAME = '/My/Secret/Parameter';
6+
57
class CreatingStack extends cdk.Stack {
68
constructor(scope: cdk.App, id: string) {
79
super(scope, id);
810

911
new ssm.StringParameter(this, 'String', {
1012
parameterName: '/My/Public/Parameter',
11-
stringValue: 'abcdef',
13+
stringValue: 'Abc123',
14+
});
15+
16+
new integ.AwsApiCall(this, 'SecureParam', {
17+
service: 'SSM',
18+
api: 'putParameter',
19+
parameters: {
20+
Name: SECURE_PARAM_NAME,
21+
Type: 'SecureString',
22+
Value: 'Abc123',
23+
},
1224
});
1325
}
1426
}
@@ -24,7 +36,6 @@ class UsingStack extends cdk.Stack {
2436
default: 1,
2537
}).valueAsNumber;
2638

27-
/// !show
2839
// Retrieve the latest value of the non-secret parameter
2940
// with name "/My/String/Parameter".
3041
const stringValue = ssm.StringParameter.fromStringParameterAttributes(this, 'MyValue', {
@@ -38,33 +49,54 @@ class UsingStack extends cdk.Stack {
3849
}).stringValue;
3950

4051
// Retrieve a specific version of the secret (SecureString) parameter.
41-
// 'version' is always required.
4252
const secretValue = ssm.StringParameter.fromSecureStringParameterAttributes(this, 'MySecureValue', {
4353
parameterName: '/My/Secret/Parameter',
44-
version: 5,
45-
});
54+
}).stringValue;
55+
const secretValueVersion = ssm.StringParameter.fromSecureStringParameterAttributes(this, 'MySecureValueVersion', {
56+
parameterName: '/My/Secret/Parameter',
57+
version: 1,
58+
}).stringValue;
4659
const secretValueVersionFromToken = ssm.StringParameter.fromSecureStringParameterAttributes(this, 'MySecureValueVersionFromToken', {
4760
parameterName: '/My/Secret/Parameter',
4861
// parameter version from token
4962
version: parameterVersion,
50-
});
51-
52-
/// !hide
53-
54-
new cdk.CfnResource(this, 'Dummy', { type: 'AWS::SNS::Topic' });
55-
new cdk.CfnOutput(this, 'TheValue', { value: stringValue });
56-
new cdk.CfnOutput(this, 'TheValueVersionFromToken', { value: stringValueVersionFromToken });
63+
}).stringValue;
5764

58-
// Cannot be provisioned so cannot be actually used
59-
Array.isArray(secretValue);
60-
Array.isArray(secretValueVersionFromToken);
65+
const user = new cdk.CfnResource(this, 'DummyResourceUsingStringParameters', {
66+
type: 'AWS::IAM::User',
67+
properties: {
68+
LoginProfile: {
69+
Password: cdk.Fn.join('-', [
70+
stringValue,
71+
stringValueVersionFromToken,
72+
secretValue,
73+
secretValueVersion,
74+
secretValueVersionFromToken,
75+
]),
76+
},
77+
},
78+
});
79+
user.applyRemovalPolicy(cdk.RemovalPolicy.DESTROY);
6180
}
6281
}
6382

6483
const app = new cdk.App();
6584

6685
const creating = new CreatingStack(app, 'sspms-creating');
86+
6787
const using = new UsingStack(app, 'sspms-using');
6888
using.addDependency(creating);
6989

90+
const cleanup = new cdk.Stack(app, 'sspms-cleanup');
91+
cleanup.addDependency(using);
92+
93+
const integTest = new integ.IntegTest(app, 'SSMParameterStoreTest', {
94+
assertionStack: cleanup,
95+
testCases: [using],
96+
});
97+
98+
integTest.assertions.awsApiCall('SSM', 'deleteParameter', {
99+
Name: SECURE_PARAM_NAME,
100+
});
101+
70102
app.synth();

0 commit comments

Comments
 (0)