Skip to content

Commit

Permalink
Update ssm-context to prevent raising an error on missing parameter
Browse files Browse the repository at this point in the history
Updates StringParameter.valueFromLookup with an optional "defaultValue"
When specified this value will be used:
 - in place of the standard dummyValue
 - to signal that an Error should not be raised during synthesis
   if the parameter is not found in the account.

Test are updated to prove that this works
  • Loading branch information
dsilbergleithcu-godaddy committed Sep 11, 2024
1 parent 473cf1d commit 4dd29a3
Show file tree
Hide file tree
Showing 4 changed files with 63 additions and 7 deletions.
9 changes: 7 additions & 2 deletions packages/aws-cdk-lib/aws-ssm/lib/parameter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -574,12 +574,17 @@ export class StringParameter extends ParameterBase implements IStringParameter {
*
* Requires that the stack this scope is defined in will have explicit
* account/region information. Otherwise, it will fail during synthesis.
*
* If defaultValue is provided, it will be used as the dummyValue
* and the ContextProvider will be told NOT to raise an error on synthesis
* if the SSM Parameter is not found in the account at synth time.
*/
public static valueFromLookup(scope: Construct, parameterName: string): string {
public static valueFromLookup(scope: Construct, parameterName: string, defaultValue?: string): string {
const value = ContextProvider.getValue(scope, {
provider: cxschema.ContextProvider.SSM_PARAMETER_PROVIDER,
props: { parameterName },
dummyValue: `dummy-value-for-${parameterName}`,
dummyValue: defaultValue || `dummy-value-for-${parameterName}`,
ignoreErrorOnMissingContext: !!defaultValue,
}).value;

return value;
Expand Down
27 changes: 27 additions & 0 deletions packages/aws-cdk-lib/aws-ssm/test/parameter.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -725,6 +725,33 @@ test('fromLookup will use the SSM context provider to read value during synthesi
key: 'ssm:account=12344:parameterName=my-param-name:region=us-east-1',
props: {
account: '12344',
ignoreErrorOnMissingContext: false,
dummyValue: 'dummy-value-for-my-param-name',
region: 'us-east-1',
parameterName: 'my-param-name',
},
provider: 'ssm',
},
]);
});

test('fromLookup will return defaultValue when it is provided', () => {
// GIVEN
const app = new cdk.App({ context: { [cxapi.NEW_STYLE_STACK_SYNTHESIS_CONTEXT]: false } });
const stack = new cdk.Stack(app, 'my-staq', { env: { region: 'us-east-1', account: '12344' } });

// WHEN
const value = ssm.StringParameter.valueFromLookup(stack, 'my-param-name', 'some-default-value');

// THEN
expect(value).toEqual('some-default-value');
expect(app.synth().manifest.missing).toEqual([
{
key: 'ssm:account=12344:parameterName=my-param-name:region=us-east-1',
props: {
account: '12344',
ignoreErrorOnMissingContext: true,
dummyValue: 'some-default-value',
region: 'us-east-1',
parameterName: 'my-param-name',
},
Expand Down
23 changes: 20 additions & 3 deletions packages/aws-cdk-lib/core/lib/context-provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,17 @@ export interface GetContextKeyOptions {
export interface GetContextValueOptions extends GetContextKeyOptions {
/**
* The value to return if the context value was not found and a missing
* context is reported. This should be a dummy value that should preferably
* fail during deployment since it represents an invalid state.
* context is reported.
*/
readonly dummyValue: any;

/**
* When True, the context provider will not throw an error if missing context
* is reported
*
* @default false
*/
readonly ignoreErrorOnMissingContext?: boolean;
}

/**
Expand Down Expand Up @@ -101,10 +108,20 @@ export class ContextProvider {
// if context is missing or an error occurred during context retrieval,
// report and return a dummy value.
if (value === undefined || providerError !== undefined) {
// build a version of the props which includes the dummyValue and ignoreError flag
const extendedProps: { [p: string]: any } = {
dummyValue: options.dummyValue,
ignoreErrorOnMissingContext: options.ignoreErrorOnMissingContext,
...props,
};

// We'll store the extendedProps in the missingContextKey report
// so that we can retrieve the dummyValue and ignoreError flag
// in the aws-cdk's ssm-context provider
stack.reportMissingContextKey({
key,
provider: options.provider as cxschema.ContextProvider,
props: props as cxschema.ContextQueryProperties,
props: extendedProps as cxschema.ContextQueryProperties,
});

if (providerError !== undefined) {
Expand Down
11 changes: 9 additions & 2 deletions packages/aws-cdk/lib/context-providers/ssm-parameters.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,17 +16,24 @@ export class SSMContextProviderPlugin implements ContextProviderPlugin {
public async getValue(args: cxschema.SSMParameterContextQuery) {
const region = args.region;
const account = args.account;

if (!('parameterName' in args)) {
throw new Error('parameterName must be provided in props for SSMContextProviderPlugin');
}
const parameterName = args.parameterName;
debug(`Reading SSM parameter ${account}:${region}:${parameterName}`);

const response = await this.getSsmParameterValue(account, region, parameterName, args.lookupRoleArn);
if (!response.Parameter || response.Parameter.Value === undefined) {
const parameterNotFound: boolean = !response.Parameter || response.Parameter.Value === undefined;
const suppressError = 'ignoreErrorOnMissingContext' in args && args.ignoreErrorOnMissingContext as boolean
if (parameterNotFound && suppressError && 'dummyValue' in args) {
return args.dummyValue;
}
if (parameterNotFound) {
throw new Error(`SSM parameter not available in account ${account}, region ${region}: ${parameterName}`);
}
return response.Parameter.Value;
// will not be undefined because we've handled undefined cases above
return response.Parameter!.Value;
}

/**
Expand Down

0 comments on commit 4dd29a3

Please sign in to comment.