diff --git a/packages/@aws-cdk/assets-docker/lib/image-asset.ts b/packages/@aws-cdk/assets-docker/lib/image-asset.ts index ef895e0e88a40..b1ecc35b652fb 100644 --- a/packages/@aws-cdk/assets-docker/lib/image-asset.ts +++ b/packages/@aws-cdk/assets-docker/lib/image-asset.ts @@ -61,7 +61,7 @@ export class DockerImageAsset extends cdk.Construct { this.node.addMetadata(cxapi.ASSET_METADATA, asset); // parse repository name and tag from the parameter (:) - const components = cdk.Fn.split(':', imageNameParameter.valueAsString); + const components = cdk.Fn.split(':', imageNameParameter.stringValue); const repositoryName = cdk.Fn.select(0, components).toString(); const imageTag = cdk.Fn.select(1, components).toString(); diff --git a/packages/@aws-cdk/assets/lib/asset.ts b/packages/@aws-cdk/assets/lib/asset.ts index d212171d738c9..d70818cefcae4 100644 --- a/packages/@aws-cdk/assets/lib/asset.ts +++ b/packages/@aws-cdk/assets/lib/asset.ts @@ -109,9 +109,9 @@ export class Asset extends cdk.Construct { description: `S3 key for asset version "${this.node.path}"` }); - this.s3BucketName = bucketParam.value.toString(); - this.s3Prefix = cdk.Fn.select(0, cdk.Fn.split(cxapi.ASSET_PREFIX_SEPARATOR, keyParam.valueAsString)).toString(); - const s3Filename = cdk.Fn.select(1, cdk.Fn.split(cxapi.ASSET_PREFIX_SEPARATOR, keyParam.valueAsString)).toString(); + this.s3BucketName = bucketParam.stringValue; + this.s3Prefix = cdk.Fn.select(0, cdk.Fn.split(cxapi.ASSET_PREFIX_SEPARATOR, keyParam.stringValue)).toString(); + const s3Filename = cdk.Fn.select(1, cdk.Fn.split(cxapi.ASSET_PREFIX_SEPARATOR, keyParam.stringValue)).toString(); this.s3ObjectKey = `${this.s3Prefix}${s3Filename}`; this.bucket = s3.Bucket.import(this, 'AssetBucket', { diff --git a/packages/@aws-cdk/aws-secretsmanager/README.md b/packages/@aws-cdk/aws-secretsmanager/README.md index 872c7965a5dc9..a5c71b8a2b6f1 100644 --- a/packages/@aws-cdk/aws-secretsmanager/README.md +++ b/packages/@aws-cdk/aws-secretsmanager/README.md @@ -5,15 +5,19 @@ const secretsmanager = require('@aws-cdk/aws-secretsmanager'); ``` ### Create a new Secret in a Stack - -In order to have SecretsManager generate a new secret value automatically, you can get started with the following: +In order to have SecretsManager generate a new secret value automatically, +you can get started with the following: [example of creating a secret](test/integ.secret.lit.ts) -The `Secret` construct does not allow specifying the `SecretString` property of the `AWS::SecretsManager::Secret` -resource as this will almost always lead to the secret being surfaced in plain text and possibly committed to your -source control. If you need to use a pre-existing secret, the recommended way is to manually provision -the secret in *AWS SecretsManager* and use the `Secret.import` method to make it available in your CDK Application: +The `Secret` construct does not allow specifying the `SecretString` property +of the `AWS::SecretsManager::Secret` resource (as this will almost always +lead to the secret being surfaced in plain text and possibly committed to +your source control). + +If you need to use a pre-existing secret, the recommended way is to manually +provision the secret in *AWS SecretsManager* and use the `Secret.import` +method to make it available in your CDK Application: ```ts const secret = Secret.import(scope, 'ImportedSecret', { @@ -22,3 +26,6 @@ const secret = Secret.import(scope, 'ImportedSecret', { encryptionKey, }); ``` + +SecretsManager secret values can only be used in select set of properties. For the +list of properties, see [the CloudFormation Dynamic References documentation](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/dynamic-references.htm). \ No newline at end of file diff --git a/packages/@aws-cdk/aws-secretsmanager/lib/secret-string.ts b/packages/@aws-cdk/aws-secretsmanager/lib/secret-string.ts index fc001572bc574..1ece79d6c3bc2 100644 --- a/packages/@aws-cdk/aws-secretsmanager/lib/secret-string.ts +++ b/packages/@aws-cdk/aws-secretsmanager/lib/secret-string.ts @@ -44,7 +44,7 @@ export class SecretString extends cdk.DynamicReference { /** * Return the full value of the secret */ - public get value(): string { + public get stringValue(): string { return this.resolveStringForJsonKey(''); } diff --git a/packages/@aws-cdk/aws-secretsmanager/lib/secret.ts b/packages/@aws-cdk/aws-secretsmanager/lib/secret.ts index 2eda7901c5d42..142592b8a6a29 100644 --- a/packages/@aws-cdk/aws-secretsmanager/lib/secret.ts +++ b/packages/@aws-cdk/aws-secretsmanager/lib/secret.ts @@ -20,10 +20,21 @@ export interface ISecret extends cdk.IConstruct { readonly secretArn: string; /** - * Returns a SecretString corresponding to this secret, so that the secret value can be referred to from other parts - * of the application (such as an RDS instance's master user password property). + * Returns a SecretString corresponding to this secret. + * + * SecretString represents the value of the Secret. + */ + readonly secretString: SecretString; + + /** + * Retrieve the value of the Secret, as a string. */ - toSecretString(): SecretString; + readonly stringValue: string; + + /** + * Interpret the secret as a JSON object and return a field's value from it + */ + jsonFieldValue(key: string): string; /** * Exports this secret. @@ -97,7 +108,7 @@ export abstract class SecretBase extends cdk.Construct implements ISecret { public abstract readonly encryptionKey?: kms.IEncryptionKey; public abstract readonly secretArn: string; - private secretString?: SecretString; + private _secretString?: SecretString; public abstract export(): SecretImportProps; @@ -127,9 +138,17 @@ export abstract class SecretBase extends cdk.Construct implements ISecret { } } - public toSecretString() { - this.secretString = this.secretString || new SecretString(this, 'SecretString', { secretId: this.secretArn }); - return this.secretString; + public get secretString() { + this._secretString = this._secretString || new SecretString(this, 'SecretString', { secretId: this.secretArn }); + return this._secretString; + } + + public get stringValue() { + return this.secretString.stringValue; + } + + public jsonFieldValue(key: string): string { + return this.secretString.jsonFieldValue(key); } } diff --git a/packages/@aws-cdk/aws-secretsmanager/test/example.app-with-secret.lit.ts b/packages/@aws-cdk/aws-secretsmanager/test/example.app-with-secret.lit.ts index cdedbb62611c4..bcaca6be74842 100644 --- a/packages/@aws-cdk/aws-secretsmanager/test/example.app-with-secret.lit.ts +++ b/packages/@aws-cdk/aws-secretsmanager/test/example.app-with-secret.lit.ts @@ -1,16 +1,26 @@ +import iam = require('@aws-cdk/aws-iam'); import cdk = require('@aws-cdk/cdk'); import secretsmanager = require('../lib'); -const app = new cdk.App(); -const stack = new cdk.Stack(app, 'aws-cdk-rds-integ'); +class ExampleStack extends cdk.Stack { + constructor(scope: cdk.App, id: string) { + super(scope, id); + + /// !show + const loginSecret = new secretsmanager.SecretString(this, 'Secret', { + secretId: 'SomeLogin' + }); -/// !show -const loginSecret = new secretsmanager.SecretString(stack, 'Secret', { secretId: 'SomeLogin', }); + new iam.User(this, 'User', { + // Get the 'password' field from the secret that looks like + // { "username": "XXXX", "password": "YYYY" } + password: loginSecret.jsonFieldValue('password') + }); + /// !hide -// DO NOT ACTUALLY DO THIS, as this will expose your secret. -// This code only exists to show how the secret would be used. -new cdk.Output(stack, 'SecretUsername', { value: loginSecret.jsonFieldValue('username') }); -new cdk.Output(stack, 'SecretPassword', { value: loginSecret.jsonFieldValue('password') }); -/// !hide + } +} +const app = new cdk.App(); +new ExampleStack(app, 'aws-cdk-secret-integ'); app.run(); \ No newline at end of file diff --git a/packages/@aws-cdk/aws-secretsmanager/test/integ.secret.lit.expected.json b/packages/@aws-cdk/aws-secretsmanager/test/integ.secret.lit.expected.json index ee909bc0f6940..2eb92c11cc1b1 100644 --- a/packages/@aws-cdk/aws-secretsmanager/test/integ.secret.lit.expected.json +++ b/packages/@aws-cdk/aws-secretsmanager/test/integ.secret.lit.expected.json @@ -60,6 +60,25 @@ "Properties": { "GenerateSecretString": {} } + }, + "User00B015A1": { + "Type": "AWS::IAM::User", + "Properties": { + "LoginProfile": { + "Password": { + "Fn::Join": [ + "", + [ + "{{resolve:secretsmanager:", + { + "Ref": "SecretA720EF05" + }, + ":SecretString:::}}" + ] + ] + } + } + } } } } \ No newline at end of file diff --git a/packages/@aws-cdk/aws-secretsmanager/test/integ.secret.lit.ts b/packages/@aws-cdk/aws-secretsmanager/test/integ.secret.lit.ts index 6000fd94003c5..4a90c8f6ef424 100644 --- a/packages/@aws-cdk/aws-secretsmanager/test/integ.secret.lit.ts +++ b/packages/@aws-cdk/aws-secretsmanager/test/integ.secret.lit.ts @@ -2,13 +2,23 @@ import iam = require('@aws-cdk/aws-iam'); import cdk = require('@aws-cdk/cdk'); import secretsManager = require('../lib'); -const app = new cdk.App(); -const stack = new cdk.Stack(app, 'Integ-SecretsManager-Secret'); -const role = new iam.Role(stack, 'TestRole', { assumedBy: new iam.AccountRootPrincipal() }); +class SecretsManagerStack extends cdk.Stack { + constructor(scope: cdk.App, id: string) { + super(scope, id); + + const role = new iam.Role(this, 'TestRole', { assumedBy: new iam.AccountRootPrincipal() }); -/// !show -const secret = new secretsManager.Secret(stack, 'Secret'); -secret.grantRead(role); -/// !hide + /// !show + const secret = new secretsManager.Secret(this, 'Secret'); + secret.grantRead(role); + new iam.User(this, 'User', { + password: secret.stringValue + }); + /// !hide + } +} + +const app = new cdk.App(); +new SecretsManagerStack(app, 'Integ-SecretsManager-Secret'); app.run(); diff --git a/packages/@aws-cdk/aws-secretsmanager/test/test.secret-string.ts b/packages/@aws-cdk/aws-secretsmanager/test/test.secret-string.ts index 2a0eb8f0afcfd..51f2677b41a20 100644 --- a/packages/@aws-cdk/aws-secretsmanager/test/test.secret-string.ts +++ b/packages/@aws-cdk/aws-secretsmanager/test/test.secret-string.ts @@ -13,7 +13,7 @@ export = { }); // THEN - test.equal(ref.node.resolve(ref.value), '{{resolve:secretsmanager:SomeSecret:SecretString:::}}'); + test.equal(ref.node.resolve(ref.stringValue), '{{resolve:secretsmanager:SomeSecret:SecretString:::}}'); test.done(); }, diff --git a/packages/@aws-cdk/aws-secretsmanager/test/test.secret.ts b/packages/@aws-cdk/aws-secretsmanager/test/test.secret.ts index 5e7695295845c..47b724900612d 100644 --- a/packages/@aws-cdk/aws-secretsmanager/test/test.secret.ts +++ b/packages/@aws-cdk/aws-secretsmanager/test/test.secret.ts @@ -224,7 +224,7 @@ export = { new cdk.Resource(stack, 'FakeResource', { type: 'CDK::Phony::Resource', properties: { - value: secret.toSecretString().value + value: secret.stringValue } }); diff --git a/packages/@aws-cdk/aws-ssm/README.md b/packages/@aws-cdk/aws-ssm/README.md index a292d7a8d441e..82f9cb74ac8b6 100644 --- a/packages/@aws-cdk/aws-ssm/README.md +++ b/packages/@aws-cdk/aws-ssm/README.md @@ -14,12 +14,23 @@ Import it into your code: import ssm = require('@aws-cdk/aws-ssm'); ``` -### Creating SSM Parameters -You can use either the `ssm.StringParameter` or `ssm.StringListParameter` (AWS CloudFormation does not support creating -*Secret-String* SSM parameters, as those would require the secret value to be inlined in the template document) classes -to register new SSM Parameters into your application: +### Using existing SSM Parameters in your CDK app + +You can reference existing SSM Parameter Store values that you want to use in +your CDK app by using `ssm.ParameterStoreString`: + +[using SSM parameter](test/integ.parameter-store-string.lit.ts) + +### Creating new SSM Parameters in your CDK app + +You can create either `ssm.StringParameter` or `ssm.StringListParameter`s in +a CDK app. These are public (not secret) values. Parameters of type +*SecretString* cannot be created directly from a CDK application; if you want +to provision secrets automatically, use Secrets Manager Secrets (see the +`@aws-cdk/aws-secretsmanager` package). [creating SSM parameters](test/integ.parameter.lit.ts) -When specifying an `allowedPattern`, the values provided as string literals are validated against the pattern and an -exception is raised if a value provided does not comply. +When specifying an `allowedPattern`, the values provided as string literals +are validated against the pattern and an exception is raised if a value +provided does not comply. diff --git a/packages/@aws-cdk/aws-ssm/lib/parameter-store-string.ts b/packages/@aws-cdk/aws-ssm/lib/parameter-store-string.ts index 1ac1263b27830..a976e5e59869b 100644 --- a/packages/@aws-cdk/aws-ssm/lib/parameter-store-string.ts +++ b/packages/@aws-cdk/aws-ssm/lib/parameter-store-string.ts @@ -11,21 +11,43 @@ export interface ParameterStoreStringProps { /** * The version number of the value you wish to retrieve. + * + * @default The latest version will be retrieved. */ - version: number; + version?: number; } /** - * References a secret value in AWS Systems Manager Parameter Store + * References a public value in AWS Systems Manager Parameter Store * * @see https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/dynamic-references.html */ -export class ParameterStoreString extends cdk.DynamicReference { +export class ParameterStoreString extends cdk.Construct { + public readonly stringValue: string; + constructor(scope: cdk.Construct, id: string, props: ParameterStoreStringProps) { - super(scope, id, { - service: cdk.DynamicReferenceService.Ssm, - referenceKey: `${props.parameterName}:${props.version}`, - }); + super(scope, id); + + // We use a different inner construct depend on whether we want the latest + // or a specific version. + // + // * Latest - generate a Parameter and reference that. + // * Specific - use a Dynamic Reference. + if (props.version === undefined) { + // Construct/get a singleton parameter under the stack + const param = new cdk.Parameter(this, 'Parameter', { + type: 'AWS::SSM::Parameter::Value', + default: props.parameterName + }); + this.stringValue = param.stringValue; + } else { + // Use a dynamic reference + const dynRef = new cdk.DynamicReference(this, 'Reference', { + service: cdk.DynamicReferenceService.Ssm, + referenceKey: `${props.parameterName}:${props.version}`, + }); + this.stringValue = dynRef.stringValue; + } } } @@ -47,6 +69,9 @@ export interface ParameterStoreSecureStringProps { /** * References a secret value in AWS Systems Manager Parameter Store * + * It is not possible to retrieve the "latest" value of a secret. + * Use Secrets Manager if you need that ability. + * * @see https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/dynamic-references.html */ export class ParameterStoreSecureString extends cdk.DynamicReference { diff --git a/packages/@aws-cdk/aws-ssm/lib/parameter.ts b/packages/@aws-cdk/aws-ssm/lib/parameter.ts index 4dfabdf0460a4..2ef28b329928e 100644 --- a/packages/@aws-cdk/aws-ssm/lib/parameter.ts +++ b/packages/@aws-cdk/aws-ssm/lib/parameter.ts @@ -43,7 +43,7 @@ export interface IStringParameter extends IParameter { /** * The parameter value. Value must not nest another parameter. Do not use {{}} in the value. */ - readonly parameterValue: string; + readonly stringValue: string; } /** @@ -54,7 +54,7 @@ export interface IStringListParameter extends IParameter { * The parameter value. Value must not nest another parameter. Do not use {{}} in the value. Values in the array * cannot contain commas (``,``). */ - readonly parameterValue: string[]; + readonly stringListValue: string[]; } /** @@ -91,7 +91,7 @@ export interface StringParameterProps extends ParameterProps { /** * The value of the parameter. It may not reference another parameter and ``{{}}`` cannot be used in the value. */ - value: string; + stringValue: string; } /** @@ -101,7 +101,7 @@ export interface StringListParameterProps extends ParameterProps { /** * The values of the parameter. It may not reference another parameter and ``{{}}`` cannot be used in the value. */ - value: string[]; + stringListValue: string[]; } /** @@ -145,13 +145,13 @@ export abstract class ParameterBase extends cdk.Construct implements IParameter export class StringParameter extends ParameterBase implements IStringParameter { public readonly parameterName: string; public readonly parameterType: string; - public readonly parameterValue: string; + public readonly stringValue: string; constructor(scope: cdk.Construct, id: string, props: StringParameterProps) { super(scope, id, props); if (props.allowedPattern) { - _assertValidValue(props.value, props.allowedPattern); + _assertValidValue(props.stringValue, props.allowedPattern); } const resource = new ssm.CfnParameter(this, 'Resource', { @@ -159,12 +159,12 @@ export class StringParameter extends ParameterBase implements IStringParameter { description: props.description, name: props.name, type: 'String', - value: props.value, + value: props.stringValue, }); this.parameterName = resource.parameterName; this.parameterType = resource.parameterType; - this.parameterValue = resource.parameterValue; + this.stringValue = resource.parameterValue; } } @@ -174,17 +174,17 @@ export class StringParameter extends ParameterBase implements IStringParameter { export class StringListParameter extends ParameterBase implements IStringListParameter { public readonly parameterName: string; public readonly parameterType: string; - public readonly parameterValue: string[]; + public readonly stringListValue: string[]; constructor(scope: cdk.Construct, id: string, props: StringListParameterProps) { super(scope, id, props); - if (props.value.find(str => !cdk.unresolved(str) && str.indexOf(',') !== -1)) { + if (props.stringListValue.find(str => !cdk.unresolved(str) && str.indexOf(',') !== -1)) { throw new Error('Values of a StringList SSM Parameter cannot contain the \',\' character. Use a string parameter instead.'); } - if (props.allowedPattern && !cdk.unresolved(props.value)) { - props.value.forEach(str => _assertValidValue(str, props.allowedPattern!)); + if (props.allowedPattern && !cdk.unresolved(props.stringListValue)) { + props.stringListValue.forEach(str => _assertValidValue(str, props.allowedPattern!)); } const resource = new ssm.CfnParameter(this, 'Resource', { @@ -192,12 +192,12 @@ export class StringListParameter extends ParameterBase implements IStringListPar description: props.description, name: props.name, type: 'StringList', - value: props.value.join(','), + value: props.stringListValue.join(','), }); this.parameterName = resource.parameterName; this.parameterType = resource.parameterType; - this.parameterValue = cdk.Fn.split(',', resource.parameterValue); + this.stringListValue = cdk.Fn.split(',', resource.parameterValue); } } diff --git a/packages/@aws-cdk/aws-ssm/test/integ.parameter-store-string.lit.expected.json b/packages/@aws-cdk/aws-ssm/test/integ.parameter-store-string.lit.expected.json new file mode 100644 index 0000000000000..85a93ef6c2476 --- /dev/null +++ b/packages/@aws-cdk/aws-ssm/test/integ.parameter-store-string.lit.expected.json @@ -0,0 +1,29 @@ +[ + { + "Resources": { + "String0BA8456E": { + "Type": "AWS::SSM::Parameter", + "Properties": { + "Type": "String", + "Value": "abcdef", + "Name": "/My/Public/Parameter" + } + } + } + }, + { + "Parameters": { + "MyValueParameterCCC58B13": { + "Type": "AWS::SSM::Parameter::Value", + "Default": "/My/Public/Parameter" + } + }, + "Outputs": { + "TheValue": { + "Value": { + "Ref": "MyValueParameterCCC58B13" + } + } + } + } +] diff --git a/packages/@aws-cdk/aws-ssm/test/integ.parameter-store-string.lit.ts b/packages/@aws-cdk/aws-ssm/test/integ.parameter-store-string.lit.ts new file mode 100644 index 0000000000000..4a1c7397f85b9 --- /dev/null +++ b/packages/@aws-cdk/aws-ssm/test/integ.parameter-store-string.lit.ts @@ -0,0 +1,48 @@ +import cdk = require('@aws-cdk/cdk'); +import ssm = require('../lib'); + +class CreatingStack extends cdk.Stack { + constructor(scope: cdk.App, id: string) { + super(scope, id); + + new ssm.StringParameter(this, 'String', { + name: '/My/Public/Parameter', + stringValue: 'abcdef' + }); + } +} + +class UsingStack extends cdk.Stack { + constructor(scope: cdk.App, id: string) { + super(scope, id); + + /// !show + // Retrieve the latest value of the non-secret parameter + // with name "/My/String/Parameter". + const stringValue = new ssm.ParameterStoreString(this, 'MyValue', { + parameterName: '/My/Public/Parameter', + // 'version' can be specified but is optional. + }).stringValue; + + // Retrieve a specific version of the secret (SecureString) parameter. + // 'version' is always required. + const secretValue = new ssm.ParameterStoreSecureString(this, 'SecretValue', { + parameterName: '/My/Secret/Parameter', + version: 5 + }).stringValue; + /// !hide + + new cdk.Output(this, 'TheValue', { value: stringValue }); + + // Cannot be provisioned so cannot be actually used + Array.isArray(secretValue); + } +} + +const app = new cdk.App(); + +const creating = new CreatingStack(app, 'sspms-creating'); +const using = new UsingStack(app, 'sspms-using'); +using.addDependency(creating); + +app.run(); \ No newline at end of file diff --git a/packages/@aws-cdk/aws-ssm/test/integ.parameter.lit.ts b/packages/@aws-cdk/aws-ssm/test/integ.parameter.lit.ts index 8719f2bee9c8b..473387a179820 100644 --- a/packages/@aws-cdk/aws-ssm/test/integ.parameter.lit.ts +++ b/packages/@aws-cdk/aws-ssm/test/integ.parameter.lit.ts @@ -14,7 +14,7 @@ const role = new iam.Role(stack, 'UserRole', { const param = new ssm.StringParameter(stack, 'StringParameter', { // description: 'Some user-friendly description', // name: 'ParameterName', - value: 'Initial parameter value', + stringValue: 'Initial parameter value', // allowedPattern: '.*', }); @@ -25,13 +25,13 @@ param.grantRead(role); const listParameter = new ssm.StringListParameter(stack, 'StringListParameter', { // description: 'Some user-friendly description', // name: 'ParameterName', - value: ['Initial parameter value A', 'Initial parameter value B'], + stringListValue: ['Initial parameter value A', 'Initial parameter value B'], // allowedPattern: '.*', }); /// !hide new cdk.Output(stack, 'StringListOutput', { - value: cdk.Fn.join('+', listParameter.parameterValue), + value: cdk.Fn.join('+', listParameter.stringListValue), }); app.run(); diff --git a/packages/@aws-cdk/aws-ssm/test/test.parameter-store-string.ts b/packages/@aws-cdk/aws-ssm/test/test.parameter-store-string.ts index 5eaa03e8974ce..7d2d5e1a9c9c8 100644 --- a/packages/@aws-cdk/aws-ssm/test/test.parameter-store-string.ts +++ b/packages/@aws-cdk/aws-ssm/test/test.parameter-store-string.ts @@ -1,9 +1,10 @@ +import { expect } from '@aws-cdk/assert'; import cdk = require('@aws-cdk/cdk'); import { Test } from 'nodeunit'; import ssm = require('../lib'); export = { - 'can reference SSMPS string'(test: Test) { + 'can reference SSMPS string - specific version'(test: Test) { // GIVEN const stack = new cdk.Stack(); @@ -14,7 +15,31 @@ export = { }); // THEN - test.equal(ref.node.resolve(ref.value), '{{resolve:ssm:/some/key:123}}'); + test.equal(ref.node.resolve(ref.stringValue), '{{resolve:ssm:/some/key:123}}'); + + test.done(); + }, + + 'can reference SSMPS string - latest version'(test: Test) { + // GIVEN + const stack = new cdk.Stack(); + + // WHEN + const ref = new ssm.ParameterStoreString(stack, 'Ref', { + parameterName: '/some/key', + }); + + // THEN + expect(stack).toMatch({ + Parameters: { + RefParameter407AF5C8: { + Type: "AWS::SSM::Parameter::Value", + Default: "/some/key" + } + } + }); + + test.deepEqual(ref.node.resolve(ref.stringValue), { Ref: 'RefParameter407AF5C8' }); test.done(); }, @@ -30,7 +55,7 @@ export = { }); // THEN - test.equal(ref.node.resolve(ref.value), '{{resolve:ssm-secure:/some/key:123}}'); + test.equal(ref.node.resolve(ref.stringValue), '{{resolve:ssm-secure:/some/key:123}}'); test.done(); }, diff --git a/packages/@aws-cdk/aws-ssm/test/test.parameter.ts b/packages/@aws-cdk/aws-ssm/test/test.parameter.ts index 35eacb136cad3..00adf228b595f 100644 --- a/packages/@aws-cdk/aws-ssm/test/test.parameter.ts +++ b/packages/@aws-cdk/aws-ssm/test/test.parameter.ts @@ -13,7 +13,7 @@ export = { allowedPattern: '.*', description: 'The value Foo', name: 'FooParameter', - value: 'Foo', + stringValue: 'Foo', }); // THEN @@ -32,7 +32,7 @@ export = { const stack = new cdk.Stack(); // THEN - test.throws(() => new ssm.StringParameter(stack, 'Parameter', { allowedPattern: '^Bar$', value: 'FooBar' }), + test.throws(() => new ssm.StringParameter(stack, 'Parameter', { allowedPattern: '^Bar$', stringValue: 'FooBar' }), /does not match the specified allowedPattern/); test.done(); }, @@ -42,7 +42,9 @@ export = { const stack = new cdk.Stack(); // THEN - test.doesNotThrow(() => new ssm.StringParameter(stack, 'Parameter', { allowedPattern: '^Bar$', value: new cdk.Token(() => 'Foo!').toString() })); + test.doesNotThrow(() => { + new ssm.StringParameter(stack, 'Parameter', { allowedPattern: '^Bar$', stringValue: new cdk.Token(() => 'Foo!').toString() }); + }); test.done(); }, @@ -55,7 +57,7 @@ export = { allowedPattern: '(Foo|Bar)', description: 'The values Foo and Bar', name: 'FooParameter', - value: ['Foo', 'Bar'], + stringListValue: ['Foo', 'Bar'], }); // THEN @@ -74,7 +76,7 @@ export = { const stack = new cdk.Stack(); // THEN - test.throws(() => new ssm.StringListParameter(stack, 'Parameter', { value: ['Foo,Bar'] }), + test.throws(() => new ssm.StringListParameter(stack, 'Parameter', { stringListValue: ['Foo,Bar'] }), /cannot contain the ',' character/); test.done(); }, @@ -84,7 +86,7 @@ export = { const stack = new cdk.Stack(); // THEN - test.throws(() => new ssm.StringListParameter(stack, 'Parameter', { allowedPattern: '^(Foo|Bar)$', value: ['Foo', 'FooBar'] }), + test.throws(() => new ssm.StringListParameter(stack, 'Parameter', { allowedPattern: '^(Foo|Bar)$', stringListValue: ['Foo', 'FooBar'] }), /does not match the specified allowedPattern/); test.done(); }, @@ -96,7 +98,7 @@ export = { // THEN test.doesNotThrow(() => new ssm.StringListParameter(stack, 'Parameter', { allowedPattern: '^(Foo|Bar)$', - value: ['Foo', new cdk.Token(() => 'Baz!').toString()] + stringListValue: ['Foo', new cdk.Token(() => 'Baz!').toString()] })); test.done(); }, @@ -104,7 +106,7 @@ export = { 'parameterArn is crafted correctly'(test: Test) { // GIVEN const stack = new cdk.Stack(); - const param = new ssm.StringParameter(stack, 'Parameter', { value: 'Foo' }); + const param = new ssm.StringParameter(stack, 'Parameter', { stringValue: 'Foo' }); // THEN test.deepEqual(param.node.resolve(param.parameterArn), { diff --git a/packages/@aws-cdk/cdk/lib/cloudformation/dynamic-reference.ts b/packages/@aws-cdk/cdk/lib/cloudformation/dynamic-reference.ts index c319317c16153..f3bccf0a78a41 100644 --- a/packages/@aws-cdk/cdk/lib/cloudformation/dynamic-reference.ts +++ b/packages/@aws-cdk/cdk/lib/cloudformation/dynamic-reference.ts @@ -25,7 +25,7 @@ export interface DynamicReferenceProps { * @see https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/dynamic-references.html */ export class DynamicReference extends Construct { - private readonly _value: string; + private _value: string; constructor(scope: Construct, id: string, props: DynamicReferenceProps) { super(scope, id); @@ -36,7 +36,7 @@ export class DynamicReference extends Construct { /** * The value of this dynamic reference */ - public get value(): string { + public get stringValue(): string { return this._value; } diff --git a/packages/@aws-cdk/cdk/lib/cloudformation/parameter.ts b/packages/@aws-cdk/cdk/lib/cloudformation/parameter.ts index 6931baa629760..540197efddf67 100644 --- a/packages/@aws-cdk/cdk/lib/cloudformation/parameter.ts +++ b/packages/@aws-cdk/cdk/lib/cloudformation/parameter.ts @@ -79,12 +79,12 @@ export class Parameter extends Referenceable { /** * The parameter value token represented as a string. */ - public valueAsString: string; + public stringValue: string; /** * The parameter value token represented as a string array. */ - public valueAsList: string[]; + public stringListValue: string[]; private properties: ParameterProps; @@ -100,8 +100,8 @@ export class Parameter extends Referenceable { super(scope, id); this.properties = props; this.value = new Ref(this); - this.valueAsString = this.value.toString(); - this.valueAsList = this.value.toList(); + this.stringValue = this.value.toString(); + this.stringListValue = this.value.toList(); } public toCloudFormation(): object { diff --git a/packages/@aws-cdk/cdk/lib/util/uniqueid.ts b/packages/@aws-cdk/cdk/lib/util/uniqueid.ts index 6f58d44bd7471..1fd72cb1c765d 100644 --- a/packages/@aws-cdk/cdk/lib/util/uniqueid.ts +++ b/packages/@aws-cdk/cdk/lib/util/uniqueid.ts @@ -45,7 +45,7 @@ export function makeUniqueId(components: string[]) { // transparent migration of cloudformation templates to the CDK without the // need to rename all resources. if (components.length === 1) { - return components[0]; + return removeNonAlphanumeric(components[0]); } const hash = pathHash(components); diff --git a/packages/@aws-cdk/cdk/test/cloudformation/test.dynamic-reference.ts b/packages/@aws-cdk/cdk/test/cloudformation/test.dynamic-reference.ts index 37b3751b3c167..81d42604fe881 100644 --- a/packages/@aws-cdk/cdk/test/cloudformation/test.dynamic-reference.ts +++ b/packages/@aws-cdk/cdk/test/cloudformation/test.dynamic-reference.ts @@ -13,7 +13,7 @@ export = { }); // THEN - test.equal(stack.node.resolve(ref.value), '{{resolve:ssm:a:b:c}}'); + test.equal(stack.node.resolve(ref.stringValue), '{{resolve:ssm:a:b:c}}'); test.done(); }, diff --git a/packages/@aws-cdk/cdk/test/cloudformation/test.logical-id.ts b/packages/@aws-cdk/cdk/test/cloudformation/test.logical-id.ts index 9fd0ecba1ebe8..ba610c8e195a4 100644 --- a/packages/@aws-cdk/cdk/test/cloudformation/test.logical-id.ts +++ b/packages/@aws-cdk/cdk/test/cloudformation/test.logical-id.ts @@ -143,6 +143,15 @@ const uniqueTests = { test.deepEqual(val1, 'FoobarB00mHelloWorldHorrayHorray640E99FB'); test.deepEqual(val2, 'FoobarB00mHelloWorldHorrayHorray744334FD'); test.done(); + }, + + 'non-alphanumeric characters are removed even if the ID has only one component'(test: Test) { + const scheme = new HashedAddressingScheme(); + const val1 = scheme.allocateAddress([ 'Foo-bar' ]); + + // same human part, different hash + test.deepEqual(val1, 'Foobar'); + test.done(); } };