Skip to content

Commit 405acf5

Browse files
committed
feat: Create StringParam the normal way, and use Custom Resource for replica
This is more like Secrets and DynamoDB replica.
1 parent aaecf9d commit 405acf5

File tree

6 files changed

+112
-21
lines changed

6 files changed

+112
-21
lines changed

.projen/tasks.json

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

.projenrc.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ const project = new awscdk.AwsCdkConstructLibrary({
44
authorAddress: 'https://github.com/nickwillan',
55
keywords: ['aws', 'cdk', 'parameter', 'serverless,', 'ssm'],
66
description: 'CDK Contstruct that creates a globally replicated Systems Manager String Parameter.',
7-
majorVersion: 2,
7+
majorVersion: 3,
88
cdkVersion: '2.190.0',
99
constructsVersion: '10.4.2',
1010
defaultReleaseBranch: 'main',

API.md

Lines changed: 25 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/index.ts

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,29 @@
11
import { PolicyStatement, Effect } from 'aws-cdk-lib/aws-iam';
2-
import { StringParameterProps } from 'aws-cdk-lib/aws-ssm';
2+
import { StringParameter, StringParameterProps } from 'aws-cdk-lib/aws-ssm';
33
import { AwsCustomResource, AwsCustomResourcePolicy, PhysicalResourceId } from 'aws-cdk-lib/custom-resources';
44
import { Construct } from 'constructs';
55

66
/**
77
* GlobalStringParameterProps defines the properties for the GlobalStringParameter construct.
88
*
99
* @interface GlobalStringParameterProps
10+
* @property {StringParameter} [existingStringParamObj] - An existing SSM String Parameter object to use instead of creating a new one.
1011
* @property {StringParameterProps} parameterProps - The properties for the SSM String Parameter to be created.
1112
* @property {{ [key: string]: string }} [tags] - Optional tags to be added to the SSM Parameter.
12-
* @property {string[]} regions - The list of AWS regions where the SSM Parameter should be created.
13+
* @property {string[]} replicaRegions - The list of AWS regions where the SSM Parameter should be replicated.
1314
*/
1415
export interface GlobalStringParameterProps {
16+
readonly existingStringParamObj?: StringParameter;
1517
readonly parameterProps: StringParameterProps;
1618
readonly tags?: { [key: string]: string }[];
17-
readonly regions: string[];
19+
readonly replicaRegions: string[];
1820
}
1921

2022
/**
2123
* Creates an SSM Parameter in multiple regions using Custom Resources
2224
*/
2325
export class GlobalStringParameter extends Construct {
26+
public readonly stringParameter: StringParameter;
2427
constructor(scope: Construct, id: string, props: GlobalStringParameterProps) {
2528
super(scope, id);
2629

@@ -32,11 +35,23 @@ export class GlobalStringParameter extends Construct {
3235
throw new Error('stringValue must be specified');
3336
}
3437

35-
if (!props.regions || props.regions.length === 0) {
38+
if (!props.replicaRegions || props.replicaRegions.length === 0) {
3639
throw new Error('At least one region must be specified');
3740
}
3841

39-
props.regions.forEach((region) => {
42+
const currentRegion = process.env.CDK_DEFAULT_REGION;
43+
if (props.replicaRegions.includes(currentRegion!)) {
44+
throw new Error('The current region cannot be included in the replicaRegions list');
45+
}
46+
47+
if (props.existingStringParamObj) {
48+
this.stringParameter = props.existingStringParamObj;
49+
} else {
50+
this.stringParameter = new StringParameter(this, 'GlobalSSMParameter', props.parameterProps);
51+
}
52+
53+
// Create a custom resource for each replica region
54+
props.replicaRegions.forEach((region) => {
4055
const resource = new AwsCustomResource(this, `GlobalSSMParameter-${region}`, {
4156
onCreate: {
4257
service: 'SSM',

test/index.test.ts

Lines changed: 63 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import * as cdk from 'aws-cdk-lib';
22
import { Template } from 'aws-cdk-lib/assertions';
3-
import { ParameterTier, ParameterDataType } from 'aws-cdk-lib/aws-ssm';
3+
import { ParameterTier, ParameterDataType, StringParameter } from 'aws-cdk-lib/aws-ssm';
44
import { GlobalStringParameter } from '../src/index';
55

66
describe('GlobalParameter', () => {
@@ -13,7 +13,7 @@ describe('GlobalParameter', () => {
1313
parameterProps: {
1414
stringValue: 'testValue',
1515
} as any,
16-
regions: ['us-east-1'],
16+
replicaRegions: ['us-east-1'],
1717
});
1818
}).toThrow('parameterName must be specified');
1919
});
@@ -27,12 +27,12 @@ describe('GlobalParameter', () => {
2727
parameterProps: {
2828
parameterName: 'testParameter',
2929
} as any,
30-
regions: ['us-east-1'],
30+
replicaRegions: ['us-east-1'],
3131
});
3232
}).toThrow('stringValue must be specified');
3333
});
3434

35-
it('throws an error if no regions are specified', () => {
35+
it('throws an error if the current region is included in the replicaRegions list', () => {
3636
const app = new cdk.App();
3737
const stack = new cdk.Stack(app, 'TestStack');
3838

@@ -42,7 +42,22 @@ describe('GlobalParameter', () => {
4242
parameterName: 'testParameter',
4343
stringValue: 'testValue',
4444
},
45-
regions: [],
45+
replicaRegions: ['us-east-1', process.env.CDK_DEFAULT_REGION!],
46+
});
47+
}).toThrow('The current region cannot be included in the replicaRegions list');
48+
});
49+
50+
it('throws an error if no replicaRegions are specified', () => {
51+
const app = new cdk.App();
52+
const stack = new cdk.Stack(app, 'TestStack');
53+
54+
expect(() => {
55+
new GlobalStringParameter(stack, 'TestGlobalParameter', {
56+
parameterProps: {
57+
parameterName: 'testParameter',
58+
stringValue: 'testValue',
59+
},
60+
replicaRegions: [],
4661
});
4762
}).toThrow('At least one region must be specified');
4863
});
@@ -60,18 +75,57 @@ describe('GlobalParameter', () => {
6075
dataType: ParameterDataType.TEXT,
6176
allowedPattern: '.*',
6277
},
63-
regions: ['us-east-1'],
78+
replicaRegions: ['us-east-1'],
6479
});
6580

6681
const template = Template.fromStack(stack);
6782

6883
console.log(JSON.stringify(template.toJSON(), null, 2));
6984

7085
template.hasResourceProperties('Custom::AWS', {});
86+
87+
template.hasResourceProperties('AWS::SSM::Parameter', {
88+
Type: 'String',
89+
Description: 'Test description',
90+
Tier: 'Standard',
91+
DataType: 'text',
92+
AllowedPattern: '.*',
93+
});
7194
});
7295
});
7396

74-
it('creates, updates, and deletes SSM parameters in multiple regions', () => {
97+
it('creates an SSM parameter with existing paramaters', () => {
98+
const app = new cdk.App();
99+
const stack = new cdk.Stack(app, 'TestStack');
100+
101+
const existingStringParamObj = new StringParameter(new cdk.Stack(app, 'OrigStack'), 'ExistingStringParameter', {
102+
parameterName: 'existingParameter',
103+
stringValue: 'existingValue',
104+
});
105+
106+
new GlobalStringParameter(stack, 'TestGlobalParameter', {
107+
existingStringParamObj,
108+
parameterProps: {
109+
parameterName: 'testParameter',
110+
stringValue: 'testValue',
111+
description: 'Test description',
112+
tier: ParameterTier.STANDARD,
113+
dataType: ParameterDataType.TEXT,
114+
allowedPattern: '.*',
115+
},
116+
replicaRegions: ['us-east-1'],
117+
});
118+
119+
const template = Template.fromStack(stack);
120+
121+
console.log(JSON.stringify(template.toJSON(), null, 2));
122+
123+
template.hasResourceProperties('Custom::AWS', {});
124+
125+
template.resourceCountIs('AWS::SSM::Parameter', 0);
126+
});
127+
128+
it('creates, updates, and deletes SSM parameters in multiple replicaRegions', () => {
75129
const app = new cdk.App();
76130
const stack = new cdk.Stack(app, 'TestStack');
77131

@@ -80,7 +134,7 @@ it('creates, updates, and deletes SSM parameters in multiple regions', () => {
80134
parameterName: 'testParameter',
81135
stringValue: 'testValue',
82136
},
83-
regions: ['us-east-1', 'us-west-2'],
137+
replicaRegions: ['us-east-1', 'us-west-2'],
84138
});
85139

86140
const template = Template.fromStack(stack);
@@ -98,7 +152,7 @@ it('adds a policy to the grantPrincipal', () => {
98152
parameterName: 'testParameter',
99153
stringValue: 'testValue',
100154
},
101-
regions: ['us-east-1'],
155+
replicaRegions: ['us-east-1'],
102156
});
103157

104158
const template = Template.fromStack(stack);

tsconfig.dev.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)