Skip to content

Commit

Permalink
fix(ssm): StringParameter.fromSecureStringParameterAttributes not w…
Browse files Browse the repository at this point in the history
…orking 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*
  • Loading branch information
mrgrain authored Oct 25, 2022
1 parent 2fcc701 commit b33b9b0
Show file tree
Hide file tree
Showing 21 changed files with 1,823 additions and 295 deletions.
26 changes: 25 additions & 1 deletion packages/@aws-cdk/aws-ssm/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,31 @@ This module is part of the [AWS Cloud Development Kit](https://github.com/aws/aw
You can reference existing SSM Parameter Store values that you want to use in
your CDK app by using `ssm.StringParameter.fromStringParameterAttributes`:

[using SSM parameter](test/integ.parameter-store-string.lit.ts)
```ts
// Retrieve the latest value of the non-secret parameter
// with name "/My/String/Parameter".
const stringValue = ssm.StringParameter.fromStringParameterAttributes(this, 'MyValue', {
parameterName: '/My/Public/Parameter',
// 'version' can be specified but is optional.
}).stringValue;
const stringValueVersionFromToken = ssm.StringParameter.fromStringParameterAttributes(this, 'MyValueVersionFromToken', {
parameterName: '/My/Public/Parameter',
// parameter version from token
version: parameterVersion,
}).stringValue;

// Retrieve a specific version of the secret (SecureString) parameter.
// 'version' is always required.
const secretValue = ssm.StringParameter.fromSecureStringParameterAttributes(this, 'MySecureValue', {
parameterName: '/My/Secret/Parameter',
version: 5,
});
const secretValueVersionFromToken = ssm.StringParameter.fromSecureStringParameterAttributes(this, 'MySecureValueVersionFromToken', {
parameterName: '/My/Secret/Parameter',
// parameter version from token
version: parameterVersion,
});
```

You can also reference an existing SSM Parameter Store value that matches an
[AWS specific parameter type](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/parameters-section-structure.html#aws-specific-parameter-types):
Expand Down
5 changes: 4 additions & 1 deletion packages/@aws-cdk/aws-ssm/lib/parameter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -492,7 +492,10 @@ export class StringParameter extends ParameterBase implements IStringParameter {
*/
public static fromSecureStringParameterAttributes(scope: Construct, id: string, attrs: SecureStringParameterAttributes): IStringParameter {
const version = attrs.version ? Tokenization.stringifyNumber(attrs.version) : '';
const stringValue = new CfnDynamicReference(CfnDynamicReferenceService.SSM_SECURE, `${attrs.parameterName}:${version}`).toString();
const stringValue = new CfnDynamicReference(
CfnDynamicReferenceService.SSM_SECURE,
version ? `${attrs.parameterName}:${version}` : attrs.parameterName,
).toString();

class Import extends ParameterBase {
public readonly parameterName = attrs.parameterName;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,26 @@
/// !cdk-integ *
import * as cdk from '@aws-cdk/core';
import * as integ from '@aws-cdk/integ-tests';
import * as ssm from '../lib';

const SECURE_PARAM_NAME = '/My/Secret/Parameter';

class CreatingStack extends cdk.Stack {
constructor(scope: cdk.App, id: string) {
super(scope, id);

new ssm.StringParameter(this, 'String', {
parameterName: '/My/Public/Parameter',
stringValue: 'abcdef',
stringValue: 'Abc123',
});

new integ.AwsApiCall(this, 'SecureParam', {
service: 'SSM',
api: 'putParameter',
parameters: {
Name: SECURE_PARAM_NAME,
Type: 'SecureString',
Value: 'Abc123',
},
});
}
}
Expand All @@ -24,7 +36,6 @@ class UsingStack extends cdk.Stack {
default: 1,
}).valueAsNumber;

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

// Retrieve a specific version of the secret (SecureString) parameter.
// 'version' is always required.
const secretValue = ssm.StringParameter.fromSecureStringParameterAttributes(this, 'MySecureValue', {
parameterName: '/My/Secret/Parameter',
version: 5,
});
}).stringValue;
const secretValueVersion = ssm.StringParameter.fromSecureStringParameterAttributes(this, 'MySecureValueVersion', {
parameterName: '/My/Secret/Parameter',
version: 1,
}).stringValue;
const secretValueVersionFromToken = ssm.StringParameter.fromSecureStringParameterAttributes(this, 'MySecureValueVersionFromToken', {
parameterName: '/My/Secret/Parameter',
// parameter version from token
version: parameterVersion,
});

/// !hide

new cdk.CfnResource(this, 'Dummy', { type: 'AWS::SNS::Topic' });
new cdk.CfnOutput(this, 'TheValue', { value: stringValue });
new cdk.CfnOutput(this, 'TheValueVersionFromToken', { value: stringValueVersionFromToken });
}).stringValue;

// Cannot be provisioned so cannot be actually used
Array.isArray(secretValue);
Array.isArray(secretValueVersionFromToken);
const user = new cdk.CfnResource(this, 'DummyResourceUsingStringParameters', {
type: 'AWS::IAM::User',
properties: {
LoginProfile: {
Password: cdk.Fn.join('-', [
stringValue,
stringValueVersionFromToken,
secretValue,
secretValueVersion,
secretValueVersionFromToken,
]),
},
},
});
user.applyRemovalPolicy(cdk.RemovalPolicy.DESTROY);
}
}

const app = new cdk.App();

const creating = new CreatingStack(app, 'sspms-creating');

const using = new UsingStack(app, 'sspms-using');
using.addDependency(creating);

const cleanup = new cdk.Stack(app, 'sspms-cleanup');
cleanup.addDependency(using);

const integTest = new integ.IntegTest(app, 'SSMParameterStoreTest', {
assertionStack: cleanup,
testCases: [using],
});

integTest.assertions.awsApiCall('SSM', 'deleteParameter', {
Name: SECURE_PARAM_NAME,
});

app.synth();
Loading

0 comments on commit b33b9b0

Please sign in to comment.