From 7d0680a5a858633f92aeb78353cac22b9a391fa7 Mon Sep 17 00:00:00 2001 From: Jonathan Goldwasser Date: Thu, 30 Dec 2021 14:44:12 +0100 Subject: [PATCH] feat(ssm): reference latest version of secure string parameters (#18187) Supported by CF since April 2021 but not yet ported to CDK. See https://aws.amazon.com/about-aws/whats-new/2021/04/now-reference-latest-aws-systems-manager-parameter-values-in-aws-cloudformation-templates-without-specifying-parameter-versions/ Close #17091 ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- packages/@aws-cdk/aws-ssm/lib/parameter.ts | 9 ++++++--- packages/@aws-cdk/aws-ssm/test/parameter.test.ts | 13 +++++++++++++ packages/@aws-cdk/core/README.md | 4 +++- packages/@aws-cdk/core/lib/secret-value.ts | 8 ++++---- packages/@aws-cdk/core/test/secret-value.test.ts | 11 +++++++++++ packages/aws-cdk-lib/README.md | 4 +++- 6 files changed, 40 insertions(+), 9 deletions(-) diff --git a/packages/@aws-cdk/aws-ssm/lib/parameter.ts b/packages/@aws-cdk/aws-ssm/lib/parameter.ts index 29c0eb8a33f03..99afccb82fda3 100644 --- a/packages/@aws-cdk/aws-ssm/lib/parameter.ts +++ b/packages/@aws-cdk/aws-ssm/lib/parameter.ts @@ -311,9 +311,11 @@ export interface StringParameterAttributes extends CommonStringParameterAttribut */ export interface SecureStringParameterAttributes extends CommonStringParameterAttributes { /** - * The version number of the value you wish to retrieve. This is required for secure strings. + * The version number of the value you wish to retrieve. + * + * @default - AWS CloudFormation uses the latest version of the parameter */ - readonly version: number; + readonly version?: number; /** * The encryption key that is used to encrypt this parameter @@ -365,7 +367,8 @@ export class StringParameter extends ParameterBase implements IStringParameter { * Imports a secure string parameter from the SSM parameter store. */ public static fromSecureStringParameterAttributes(scope: Construct, id: string, attrs: SecureStringParameterAttributes): IStringParameter { - const stringValue = new CfnDynamicReference(CfnDynamicReferenceService.SSM_SECURE, `${attrs.parameterName}:${Tokenization.stringifyNumber(attrs.version)}`).toString(); + const version = attrs.version ? Tokenization.stringifyNumber(attrs.version) : ''; + const stringValue = new CfnDynamicReference(CfnDynamicReferenceService.SSM_SECURE, `${attrs.parameterName}:${version}`).toString(); class Import extends ParameterBase { public readonly parameterName = attrs.parameterName; diff --git a/packages/@aws-cdk/aws-ssm/test/parameter.test.ts b/packages/@aws-cdk/aws-ssm/test/parameter.test.ts index 45fbc70f9d926..272a477523623 100644 --- a/packages/@aws-cdk/aws-ssm/test/parameter.test.ts +++ b/packages/@aws-cdk/aws-ssm/test/parameter.test.ts @@ -589,6 +589,19 @@ test('StringParameter.fromSecureStringParameterAttributes with encryption key cr }); }); +test('StringParameter.fromSecureStringParameterAttributes without version', () => { + // GIVEN + const stack = new cdk.Stack(); + + // WHEN + const param = ssm.StringParameter.fromSecureStringParameterAttributes(stack, 'MyParamName', { + parameterName: 'MyParamName', + }); + + // THEN + expect(stack.resolve(param.stringValue)).toEqual('{{resolve:ssm-secure:MyParamName:}}'); +}); + test('StringListParameter.fromName', () => { // GIVEN const stack = new cdk.Stack(); diff --git a/packages/@aws-cdk/core/README.md b/packages/@aws-cdk/core/README.md index d6b58901838c2..d2b1e2c5e5222 100644 --- a/packages/@aws-cdk/core/README.md +++ b/packages/@aws-cdk/core/README.md @@ -223,7 +223,9 @@ Using AWS Secrets Manager is the recommended way to reference secrets in a CDK a `SecretValue` also supports the following secret sources: - `SecretValue.plainText(secret)`: stores the secret as plain text in your app and the resulting template (not recommended). - - `SecretValue.ssmSecure(param, version)`: refers to a secret stored as a SecureString in the SSM Parameter Store. + - `SecretValue.ssmSecure(param, version)`: refers to a secret stored as a SecureString in the SSM + Parameter Store. If you don't specify the exact version, AWS CloudFormation uses the latest + version of the parameter. - `SecretValue.cfnParameter(param)`: refers to a secret passed through a CloudFormation parameter (must have `NoEcho: true`). - `SecretValue.cfnDynamicReference(dynref)`: refers to a secret described by a CloudFormation dynamic reference (used by `ssmSecure` and `secretsManager`). diff --git a/packages/@aws-cdk/core/lib/secret-value.ts b/packages/@aws-cdk/core/lib/secret-value.ts index b57b704bd9ed6..84e668a1b4306 100644 --- a/packages/@aws-cdk/core/lib/secret-value.ts +++ b/packages/@aws-cdk/core/lib/secret-value.ts @@ -67,11 +67,11 @@ export class SecretValue extends Intrinsic { * Parameter Store. The parameter name is case-sensitive. * * @param version An integer that specifies the version of the parameter to - * use. You must specify the exact version. You cannot currently specify that - * AWS CloudFormation use the latest version of a parameter. + * use. If you don't specify the exact version, AWS CloudFormation uses the + * latest version of the parameter. */ - public static ssmSecure(parameterName: string, version: string): SecretValue { - const parts = [parameterName, version]; + public static ssmSecure(parameterName: string, version?: string): SecretValue { + const parts = [parameterName, version ?? '']; return this.cfnDynamicReference(new CfnDynamicReference(CfnDynamicReferenceService.SSM_SECURE, parts.join(':'))); } diff --git a/packages/@aws-cdk/core/test/secret-value.test.ts b/packages/@aws-cdk/core/test/secret-value.test.ts index 2efcdffbe808d..a987cfaff0c87 100644 --- a/packages/@aws-cdk/core/test/secret-value.test.ts +++ b/packages/@aws-cdk/core/test/secret-value.test.ts @@ -119,6 +119,17 @@ describe('secret value', () => { }); + test('ssmSecure without version', () => { + // GIVEN + const stack = new Stack(); + + // WHEN + const v = SecretValue.ssmSecure('param-name'); + + // THEN + expect(stack.resolve(v)).toEqual('{{resolve:ssm-secure:param-name:}}'); + }); + test('cfnDynamicReference', () => { // GIVEN const stack = new Stack(); diff --git a/packages/aws-cdk-lib/README.md b/packages/aws-cdk-lib/README.md index d08d5b4495bb6..017a85437675c 100644 --- a/packages/aws-cdk-lib/README.md +++ b/packages/aws-cdk-lib/README.md @@ -254,7 +254,9 @@ Using AWS Secrets Manager is the recommended way to reference secrets in a CDK a `SecretValue` also supports the following secret sources: - `SecretValue.plainText(secret)`: stores the secret as plain text in your app and the resulting template (not recommended). - - `SecretValue.ssmSecure(param, version)`: refers to a secret stored as a SecureString in the SSM Parameter Store. + - `SecretValue.ssmSecure(param, version)`: refers to a secret stored as a SecureString in the SSM + Parameter Store. If you don't specify the exact version, AWS CloudFormation uses the latest + version of the parameter. - `SecretValue.cfnParameter(param)`: refers to a secret passed through a CloudFormation parameter (must have `NoEcho: true`). - `SecretValue.cfnDynamicReference(dynref)`: refers to a secret described by a CloudFormation dynamic reference (used by `ssmSecure` and `secretsManager`).