cdk-ssm-refs 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +189 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +110 -0
- package/dist/paths.d.ts +19 -0
- package/dist/paths.d.ts.map +1 -0
- package/dist/registry.d.ts +83 -0
- package/dist/registry.d.ts.map +1 -0
- package/dist/types.d.ts +52 -0
- package/dist/types.d.ts.map +1 -0
- package/package.json +40 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 DanielCaz
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
# cdk-ssm-refs
|
|
2
|
+
|
|
3
|
+
Typed registry for AWS Systems Manager parameters and Secrets Manager secrets in AWS CDK.
|
|
4
|
+
|
|
5
|
+
Declare your parameters and secrets once, and get a consistent path prefix, compile-time
|
|
6
|
+
checked keys, ready-made dynamic references, and IAM read policies scoped to exactly that
|
|
7
|
+
prefix.
|
|
8
|
+
|
|
9
|
+
## Install
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
bun add cdk-ssm-refs
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
`aws-cdk-lib`, `constructs`, and `typescript` are peer dependencies, so your CDK app
|
|
16
|
+
supplies them.
|
|
17
|
+
|
|
18
|
+
## Usage
|
|
19
|
+
|
|
20
|
+
```ts
|
|
21
|
+
import { defineParamRegistry } from 'cdk-ssm-refs';
|
|
22
|
+
|
|
23
|
+
export const params = defineParamRegistry({
|
|
24
|
+
prefix: '/team/my-app', // must start with '/'; trailing slashes are ignored
|
|
25
|
+
parameters: {
|
|
26
|
+
vpcId: 'vpc-id',
|
|
27
|
+
subnetIds: 'subnet-ids',
|
|
28
|
+
},
|
|
29
|
+
secrets: {
|
|
30
|
+
dbPassword: 'db-password',
|
|
31
|
+
},
|
|
32
|
+
});
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
The keys of `parameters` and `secrets` are the only values accepted by the `key` argument
|
|
36
|
+
of every method, so an unknown key is a compile error.
|
|
37
|
+
|
|
38
|
+
### Writing parameters
|
|
39
|
+
|
|
40
|
+
```ts
|
|
41
|
+
import { ParameterTier } from 'aws-cdk-lib/aws-ssm';
|
|
42
|
+
|
|
43
|
+
// Creates /team/my-app/vpc-id
|
|
44
|
+
params.putParameter(this, 'VpcIdParam', 'vpcId', vpc.vpcId);
|
|
45
|
+
|
|
46
|
+
// Any other StringParameterProps are passed through
|
|
47
|
+
params.putParameter(this, 'SubnetIdsParam', 'subnetIds', subnetIds.join(','), {
|
|
48
|
+
description: 'Private subnet ids',
|
|
49
|
+
tier: ParameterTier.STANDARD,
|
|
50
|
+
});
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
`putParameter` returns the `StringParameter` it creates, so you can keep working with the
|
|
54
|
+
construct. To pin one, call `applyRemovalPolicy(RemovalPolicy.RETAIN)` on the return value,
|
|
55
|
+
importing `RemovalPolicy` from `aws-cdk-lib/core`.
|
|
56
|
+
|
|
57
|
+
### Emitting stack outputs
|
|
58
|
+
|
|
59
|
+
```ts
|
|
60
|
+
const params = defineParamRegistry({
|
|
61
|
+
prefix: '/team/my-app',
|
|
62
|
+
parameters: { vpcId: 'vpc-id' },
|
|
63
|
+
emitOutputs: true,
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
params.putParameter(this, 'VpcIdParam', 'vpcId', vpc.vpcId);
|
|
67
|
+
// Also creates a CfnOutput from the construct id 'VpcIdParam-output'
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
With `emitOutputs` enabled every `putParameter` call additionally emits a `CfnOutput` whose
|
|
71
|
+
value is the raw parameter value. See the caveats below before turning this on.
|
|
72
|
+
|
|
73
|
+
### Reading parameters from another stack
|
|
74
|
+
|
|
75
|
+
```ts
|
|
76
|
+
// '{{resolve:ssm:/team/my-app/vpc-id}}'
|
|
77
|
+
const vpcId = params.parameterRef('vpcId');
|
|
78
|
+
|
|
79
|
+
// Pinned to a specific version, for drift detection
|
|
80
|
+
const pinned = params.parameterRef('vpcId', { version: 3 });
|
|
81
|
+
|
|
82
|
+
// Encrypted reference - requires a SecureString parameter
|
|
83
|
+
const secure = params.secureParameterRef('vpcId');
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
### Secrets
|
|
87
|
+
|
|
88
|
+
```ts
|
|
89
|
+
const secret = params.createSecret(this, 'DbPassword', 'dbPassword', {
|
|
90
|
+
description: 'Database password',
|
|
91
|
+
generateSecretString: { excludePunctuation: true },
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
// Use secret.secretValue directly within the creating stack
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
### IAM read policies
|
|
98
|
+
|
|
99
|
+
```ts
|
|
100
|
+
import { Stack } from 'aws-cdk-lib';
|
|
101
|
+
import * as iam from 'aws-cdk-lib/aws-iam';
|
|
102
|
+
|
|
103
|
+
const role = new iam.Role(this, 'Reader', {
|
|
104
|
+
assumedBy: new iam.ServicePrincipal('lambda.amazonaws.com'),
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
// ssm:GetParameter(s) plus secretsmanager:GetSecretValue, both scoped
|
|
108
|
+
// to the registry prefix in the region and account of the stack.
|
|
109
|
+
for (const statement of params.readPolicyStatements(Stack.of(this))) {
|
|
110
|
+
role.addToPolicy(statement);
|
|
111
|
+
}
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
The ARN patterns are also exposed on their own via `parameterArnPattern(stack)` and
|
|
115
|
+
`secretArnPattern(stack)` if you need to build a different policy.
|
|
116
|
+
|
|
117
|
+
## API
|
|
118
|
+
|
|
119
|
+
| Member | Description |
|
|
120
|
+
| ----------------------------------------------------------------- | ---------------------------------------------------------------- |
|
|
121
|
+
| `defineParamRegistry(config)` | Creates a `ParamRegistry`. |
|
|
122
|
+
| `ParamRegistry` | The class itself, if you need it as a type. |
|
|
123
|
+
| `config.prefix` | Path prefix. Must start with `/`; trailing slashes ignored. |
|
|
124
|
+
| `config.parameters` | Key to path-segment map for SSM parameters. |
|
|
125
|
+
| `config.secrets` | Key to path-segment map for Secrets Manager secrets. Optional. |
|
|
126
|
+
| `config.emitOutputs` | Emit a `CfnOutput` per `putParameter` call. Defaults to `false`. |
|
|
127
|
+
| `parameterName(key)` → `string` | Fully qualified parameter name, e.g. `/team/my-app/vpc-id`. |
|
|
128
|
+
| `putParameter(scope, id, key, value, props?)` → `StringParameter` | Creates a `StringParameter`. |
|
|
129
|
+
| `parameterRef(key, options?)` → `string` | `{{resolve:ssm:...}}` dynamic reference. |
|
|
130
|
+
| `secureParameterRef(key, options?)` → `string` | `{{resolve:ssm-secure:...}}` dynamic reference. |
|
|
131
|
+
| `createSecret(scope, id, key, props?)` → `Secret` | Creates a `Secret`. |
|
|
132
|
+
| `parameterArnPattern(stack)` → `string` | ARN pattern for every parameter under the prefix. |
|
|
133
|
+
| `secretArnPattern(stack)` → `string` | ARN pattern for every secret under the prefix. |
|
|
134
|
+
| `readPolicyStatements(stack)` → `PolicyStatement[]` | Read-only IAM statements for both patterns. |
|
|
135
|
+
| `normalizePrefix(prefix)` → `{ withSlash, withoutSlash }` | Low-level prefix normalizer used by the constructor. |
|
|
136
|
+
|
|
137
|
+
`ParamRegistryConfig` and `DynamicRefOptions` are also exported as types, for annotating
|
|
138
|
+
your own helpers.
|
|
139
|
+
|
|
140
|
+
## Caveats
|
|
141
|
+
|
|
142
|
+
- **`prefix` must start with `/`.** `defineParamRegistry` throws on `'team/my-app'`, so write
|
|
143
|
+
`'/team/my-app'`. Trailing slashes are ignored, and empty segments (`//`) are rejected.
|
|
144
|
+
- **Dynamic references resolve at deploy time.** The parameter must already exist in the
|
|
145
|
+
target account and region, so calling `putParameter` and `parameterRef` for the same
|
|
146
|
+
key in one stack will not work — the reference is resolved before the parameter is
|
|
147
|
+
created.
|
|
148
|
+
- **References are not version-pinned by default.** `parameterRef('vpcId')` resolves
|
|
149
|
+
whatever version is current at deploy time and will not report drift when the value
|
|
150
|
+
changes. Pass `{ version }` if you want that.
|
|
151
|
+
- **`version` must be an integer from 1 to 100.** CloudFormation accepts no other
|
|
152
|
+
parameter versions, so `parameterRef('vpcId', { version: 0 })` throws.
|
|
153
|
+
- **`emitOutputs` writes the value into the template.** Each `CfnOutput` carries the raw
|
|
154
|
+
parameter value, which anyone who can read the stack can see, so leave it off for
|
|
155
|
+
sensitive values. CloudFormation logical ids cannot contain `-`, so CDK strips it during
|
|
156
|
+
synthesis and the `'VpcIdParam-output'` construct id is deployed as `VpcIdParamoutput`.
|
|
157
|
+
- **`secureParameterRef` requires a `SecureString` parameter.** Plain `String`
|
|
158
|
+
parameters cannot be read through `ssm-secure`.
|
|
159
|
+
- **Secret names get a random suffix.** CloudFormation appends six characters to every
|
|
160
|
+
secret name it creates, so a secret cannot be looked up by its exact name from another
|
|
161
|
+
stack.
|
|
162
|
+
- **`StringParameterProps` has no `removalPolicy`.** Call
|
|
163
|
+
`parameter.applyRemovalPolicy(...)` on the construct returned by `putParameter`
|
|
164
|
+
instead.
|
|
165
|
+
- **`readPolicyStatements` grants `GetParameter`/`GetParameters` only.** Add
|
|
166
|
+
`ssm:GetParametersByPath` yourself if you load the whole subtree by path.
|
|
167
|
+
- **ARN patterns are pinned to the `aws` partition.** `parameterArnPattern` and
|
|
168
|
+
`secretArnPattern` return `arn:aws:...` strings built from the stack's region and account,
|
|
169
|
+
with no partition lookup, feature flag or partition pseudo parameter involved, so for a
|
|
170
|
+
stack with an explicit environment they are plain strings that are easy to assert on.
|
|
171
|
+
- **ESM only.** The package ships ESM and requires Node >= 20.19.0, which is the
|
|
172
|
+
first release where `require(esm)` works without a flag.
|
|
173
|
+
|
|
174
|
+
## Development
|
|
175
|
+
|
|
176
|
+
```bash
|
|
177
|
+
bun install
|
|
178
|
+
bun run test
|
|
179
|
+
bun run typecheck
|
|
180
|
+
bun run build
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
# License
|
|
184
|
+
|
|
185
|
+
MIT License
|
|
186
|
+
|
|
187
|
+
# Contributing
|
|
188
|
+
|
|
189
|
+
Contributions are welcome! Please open an issue or submit a pull request on GitHub. Make sure to follow the existing code style and include tests for any new functionality.
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AACnE,OAAO,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAC7C,YAAY,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
// src/registry.ts
|
|
2
|
+
import * as cdk from "aws-cdk-lib/core";
|
|
3
|
+
import * as iam from "aws-cdk-lib/aws-iam";
|
|
4
|
+
import * as sm from "aws-cdk-lib/aws-secretsmanager";
|
|
5
|
+
import * as ssm from "aws-cdk-lib/aws-ssm";
|
|
6
|
+
|
|
7
|
+
// src/paths.ts
|
|
8
|
+
function normalizePrefix(prefix) {
|
|
9
|
+
if (!prefix.startsWith("/")) {
|
|
10
|
+
throw new Error(`[cdk-ssm-refs] prefix must start with "/", received ${JSON.stringify(prefix)}`);
|
|
11
|
+
}
|
|
12
|
+
const trimmed = prefix.replace(/\/+$/, "");
|
|
13
|
+
if (trimmed.length === 0) {
|
|
14
|
+
throw new Error(`[cdk-ssm-refs] prefix must contain at least one path segment, received ${JSON.stringify(prefix)}`);
|
|
15
|
+
}
|
|
16
|
+
if (trimmed.includes("//")) {
|
|
17
|
+
throw new Error(`[cdk-ssm-refs] prefix must not contain empty path segments, received ${JSON.stringify(prefix)}`);
|
|
18
|
+
}
|
|
19
|
+
return { withSlash: trimmed, withoutSlash: trimmed.slice(1) };
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// src/registry.ts
|
|
23
|
+
var MIN_PARAM_VERSION = 1;
|
|
24
|
+
var MAX_PARAM_VERSION = 100;
|
|
25
|
+
|
|
26
|
+
class ParamRegistry {
|
|
27
|
+
config;
|
|
28
|
+
parameterPath;
|
|
29
|
+
arnPath;
|
|
30
|
+
emitOutputs;
|
|
31
|
+
constructor(config) {
|
|
32
|
+
this.config = config;
|
|
33
|
+
const { withSlash, withoutSlash } = normalizePrefix(config.prefix);
|
|
34
|
+
this.parameterPath = withSlash;
|
|
35
|
+
this.arnPath = withoutSlash;
|
|
36
|
+
this.emitOutputs = config.emitOutputs ?? false;
|
|
37
|
+
}
|
|
38
|
+
parameterName(key) {
|
|
39
|
+
const segment = this.config.parameters[key];
|
|
40
|
+
if (typeof segment !== "string" || segment.length === 0) {
|
|
41
|
+
throw new Error(`[cdk-ssm-refs] unknown parameter key: ${String(key)}`);
|
|
42
|
+
}
|
|
43
|
+
return `${this.parameterPath}/${segment}`;
|
|
44
|
+
}
|
|
45
|
+
putParameter(scope, id, key, value, props = {}) {
|
|
46
|
+
const parameterName = this.parameterName(key);
|
|
47
|
+
const parameter = new ssm.StringParameter(scope, `${id}-param`, {
|
|
48
|
+
...props,
|
|
49
|
+
parameterName,
|
|
50
|
+
stringValue: value
|
|
51
|
+
});
|
|
52
|
+
if (this.emitOutputs) {
|
|
53
|
+
new cdk.CfnOutput(scope, `${id}-output`, {
|
|
54
|
+
value,
|
|
55
|
+
description: parameterName
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
return parameter;
|
|
59
|
+
}
|
|
60
|
+
parameterRef(key, options = {}) {
|
|
61
|
+
return this.dynamicRef("ssm", key, options);
|
|
62
|
+
}
|
|
63
|
+
secureParameterRef(key, options = {}) {
|
|
64
|
+
return this.dynamicRef("ssm-secure", key, options);
|
|
65
|
+
}
|
|
66
|
+
dynamicRef(service, key, options) {
|
|
67
|
+
const { version } = options;
|
|
68
|
+
if (version !== undefined && (!Number.isInteger(version) || version < MIN_PARAM_VERSION || version > MAX_PARAM_VERSION)) {
|
|
69
|
+
throw new Error(`[cdk-ssm-refs] version must be an integer between ${MIN_PARAM_VERSION} and ${MAX_PARAM_VERSION}, received ${version}`);
|
|
70
|
+
}
|
|
71
|
+
const suffix = version === undefined ? "" : `:${version}`;
|
|
72
|
+
return `{{resolve:${service}:${this.parameterName(key)}${suffix}}}`;
|
|
73
|
+
}
|
|
74
|
+
createSecret(scope, id, key, props = {}) {
|
|
75
|
+
const segment = this.config.secrets?.[key];
|
|
76
|
+
if (typeof segment !== "string" || segment.length === 0) {
|
|
77
|
+
throw new Error(`[cdk-ssm-refs] unknown secret key: ${String(key)}`);
|
|
78
|
+
}
|
|
79
|
+
return new sm.Secret(scope, `${id}-secret`, {
|
|
80
|
+
...props,
|
|
81
|
+
secretName: `${this.parameterPath}/${segment}`
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
parameterArnPattern(stack) {
|
|
85
|
+
return `arn:aws:ssm:${stack.region}:${stack.account}:parameter/${this.arnPath}/*`;
|
|
86
|
+
}
|
|
87
|
+
secretArnPattern(stack) {
|
|
88
|
+
return `arn:aws:secretsmanager:${stack.region}:${stack.account}:secret:${this.parameterPath}/*`;
|
|
89
|
+
}
|
|
90
|
+
readPolicyStatements(stack) {
|
|
91
|
+
return [
|
|
92
|
+
new iam.PolicyStatement({
|
|
93
|
+
actions: ["ssm:GetParameter", "ssm:GetParameters"],
|
|
94
|
+
resources: [this.parameterArnPattern(stack)]
|
|
95
|
+
}),
|
|
96
|
+
new iam.PolicyStatement({
|
|
97
|
+
actions: ["secretsmanager:GetSecretValue"],
|
|
98
|
+
resources: [this.secretArnPattern(stack)]
|
|
99
|
+
})
|
|
100
|
+
];
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
function defineParamRegistry(config) {
|
|
104
|
+
return new ParamRegistry(config);
|
|
105
|
+
}
|
|
106
|
+
export {
|
|
107
|
+
ParamRegistry,
|
|
108
|
+
defineParamRegistry,
|
|
109
|
+
normalizePrefix
|
|
110
|
+
};
|
package/dist/paths.d.ts
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Validates and normalizes an SSM / Secrets Manager path prefix.
|
|
3
|
+
*
|
|
4
|
+
* A prefix must start with `/` and contain at least one path segment, so
|
|
5
|
+
* `/team/my-app` is accepted and `team/my-app` is rejected. Trailing slashes are
|
|
6
|
+
* ignored, and empty path segments (`//`) are rejected.
|
|
7
|
+
*
|
|
8
|
+
* Returns both of the forms the AWS APIs need: the leading-slash form used in
|
|
9
|
+
* parameter and secret names, and that same value with the single leading
|
|
10
|
+
* slash removed, which is how ARN resource names are written.
|
|
11
|
+
*
|
|
12
|
+
* @throws If `prefix` does not start with `/`, has no path segment, or
|
|
13
|
+
* contains an empty path segment.
|
|
14
|
+
*/
|
|
15
|
+
export declare function normalizePrefix(prefix: string): {
|
|
16
|
+
withSlash: string;
|
|
17
|
+
withoutSlash: string;
|
|
18
|
+
};
|
|
19
|
+
//# sourceMappingURL=paths.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"paths.d.ts","sourceRoot":"","sources":["../src/paths.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG;IAC/C,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,EAAE,MAAM,CAAC;CACtB,CAsBA"}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import type { Construct } from 'constructs';
|
|
2
|
+
import * as cdk from 'aws-cdk-lib/core';
|
|
3
|
+
import * as iam from 'aws-cdk-lib/aws-iam';
|
|
4
|
+
import * as sm from 'aws-cdk-lib/aws-secretsmanager';
|
|
5
|
+
import * as ssm from 'aws-cdk-lib/aws-ssm';
|
|
6
|
+
import type { DynamicRefOptions, ParamRegistryConfig } from './types.js';
|
|
7
|
+
export declare class ParamRegistry<P extends Record<string, string>, S extends Record<string, string>> {
|
|
8
|
+
private readonly config;
|
|
9
|
+
private readonly parameterPath;
|
|
10
|
+
private readonly arnPath;
|
|
11
|
+
private readonly emitOutputs;
|
|
12
|
+
constructor(config: ParamRegistryConfig<P, S>);
|
|
13
|
+
/**
|
|
14
|
+
* The fully qualified SSM parameter name for `key`,
|
|
15
|
+
* e.g. `/team/my-app/vpc-id`.
|
|
16
|
+
*/
|
|
17
|
+
parameterName(key: keyof P): string;
|
|
18
|
+
/**
|
|
19
|
+
* Creates an SSM `StringParameter` for `key` under the registry prefix.
|
|
20
|
+
*
|
|
21
|
+
* Any other `StringParameterProps` (`description`, `tier`,
|
|
22
|
+
* `allowedPattern`, `type`, `dataType`, ...) are passed straight through.
|
|
23
|
+
*
|
|
24
|
+
* Note that `StringParameterProps` does not accept a `removalPolicy`; call
|
|
25
|
+
* `parameter.applyRemovalPolicy(...)` on the returned construct instead.
|
|
26
|
+
*/
|
|
27
|
+
putParameter(scope: Construct, id: string, key: keyof P, value: string, props?: Omit<ssm.StringParameterProps, 'parameterName' | 'stringValue'>): ssm.StringParameter;
|
|
28
|
+
/**
|
|
29
|
+
* A CloudFormation dynamic reference to the parameter, e.g.
|
|
30
|
+
* `{{resolve:ssm:/team/my-app/vpc-id}}`.
|
|
31
|
+
*
|
|
32
|
+
* Dynamic references are resolved at deploy time, so the parameter must
|
|
33
|
+
* already exist in the target account and region. Calling `putParameter` and
|
|
34
|
+
* `parameterRef` for the same key in a single stack does not work: the
|
|
35
|
+
* reference is resolved before the parameter is created.
|
|
36
|
+
*/
|
|
37
|
+
parameterRef(key: keyof P, options?: DynamicRefOptions): string;
|
|
38
|
+
/**
|
|
39
|
+
* An encrypted dynamic reference to the parameter, e.g.
|
|
40
|
+
* `{{resolve:ssm-secure:/team/my-app/vpc-id}}`.
|
|
41
|
+
*
|
|
42
|
+
* The referenced parameter must have been created as a `SecureString`.
|
|
43
|
+
*/
|
|
44
|
+
secureParameterRef(key: keyof P, options?: DynamicRefOptions): string;
|
|
45
|
+
private dynamicRef;
|
|
46
|
+
/**
|
|
47
|
+
* Creates a Secrets Manager secret for `key` under the registry prefix.
|
|
48
|
+
*
|
|
49
|
+
* CloudFormation appends a random six character suffix to every secret
|
|
50
|
+
* name it creates, so the resulting name is not exactly
|
|
51
|
+
* `prefix/key` and cannot be referenced by name from another stack.
|
|
52
|
+
*/
|
|
53
|
+
createSecret(scope: Construct, id: string, key: keyof S, props?: Omit<sm.SecretProps, 'secretName'>): sm.Secret;
|
|
54
|
+
/**
|
|
55
|
+
* ARN pattern matching every parameter under the registry prefix, in the
|
|
56
|
+
* region and account of `stack`.
|
|
57
|
+
*
|
|
58
|
+
* The partition is fixed to `aws`, the US commercial partition, so no
|
|
59
|
+
* partition lookup, feature flag or pseudo parameter is involved. For a
|
|
60
|
+
* stack with an explicit environment the result is a plain string with no
|
|
61
|
+
* tokens in it; an environment agnostic stack still yields the
|
|
62
|
+
* `AWS::Region` and `AWS::AccountId` tokens. The pattern carries no
|
|
63
|
+
* permissions by itself: `readPolicyStatements` attaches read actions to
|
|
64
|
+
* it, and you can reuse it for custom read or write policies.
|
|
65
|
+
*/
|
|
66
|
+
parameterArnPattern(stack: cdk.Stack): string;
|
|
67
|
+
/**
|
|
68
|
+
* ARN pattern matching every secret under the registry prefix.
|
|
69
|
+
*
|
|
70
|
+
* Secrets Manager separates the name from the resource type with `:`
|
|
71
|
+
* rather than `/`, hence the `secret:` prefix, and the trailing wildcard
|
|
72
|
+
* covers the suffix CloudFormation appends to each name.
|
|
73
|
+
*/
|
|
74
|
+
secretArnPattern(stack: cdk.Stack): string;
|
|
75
|
+
/**
|
|
76
|
+
* Read-only IAM statements scoped to the registry prefix: `ssm:GetParameter`
|
|
77
|
+
* and `ssm:GetParameters` for the parameters, and
|
|
78
|
+
* `secretsmanager:GetSecretValue` for the secrets.
|
|
79
|
+
*/
|
|
80
|
+
readPolicyStatements(stack: cdk.Stack): iam.PolicyStatement[];
|
|
81
|
+
}
|
|
82
|
+
export declare function defineParamRegistry<P extends Record<string, string>, S extends Record<string, string> = Record<never, string>>(config: ParamRegistryConfig<P, S>): ParamRegistry<P, S>;
|
|
83
|
+
//# sourceMappingURL=registry.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../src/registry.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAC5C,OAAO,KAAK,GAAG,MAAM,kBAAkB,CAAC;AACxC,OAAO,KAAK,GAAG,MAAM,qBAAqB,CAAC;AAC3C,OAAO,KAAK,EAAE,MAAM,gCAAgC,CAAC;AACrD,OAAO,KAAK,GAAG,MAAM,qBAAqB,CAAC;AAE3C,OAAO,KAAK,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AAMzE,qBAAa,aAAa,CACxB,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAChC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC;IAMpB,OAAO,CAAC,QAAQ,CAAC,MAAM;IAJnC,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAS;IACvC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IACjC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAU;gBAET,MAAM,EAAE,mBAAmB,CAAC,CAAC,EAAE,CAAC,CAAC;IAO9D;;;OAGG;IACH,aAAa,CAAC,GAAG,EAAE,MAAM,CAAC,GAAG,MAAM;IAUnC;;;;;;;;OAQG;IACH,YAAY,CACV,KAAK,EAAE,SAAS,EAChB,EAAE,EAAE,MAAM,EACV,GAAG,EAAE,MAAM,CAAC,EACZ,KAAK,EAAE,MAAM,EACb,KAAK,GAAE,IAAI,CAAC,GAAG,CAAC,oBAAoB,EAAE,eAAe,GAAG,aAAa,CAAM,GAC1E,GAAG,CAAC,eAAe;IAmBtB;;;;;;;;OAQG;IACH,YAAY,CAAC,GAAG,EAAE,MAAM,CAAC,EAAE,OAAO,GAAE,iBAAsB,GAAG,MAAM;IAInE;;;;;OAKG;IACH,kBAAkB,CAAC,GAAG,EAAE,MAAM,CAAC,EAAE,OAAO,GAAE,iBAAsB,GAAG,MAAM;IAIzE,OAAO,CAAC,UAAU;IAuBlB;;;;;;OAMG;IACH,YAAY,CACV,KAAK,EAAE,SAAS,EAChB,EAAE,EAAE,MAAM,EACV,GAAG,EAAE,MAAM,CAAC,EACZ,KAAK,GAAE,IAAI,CAAC,EAAE,CAAC,WAAW,EAAE,YAAY,CAAM,GAC7C,EAAE,CAAC,MAAM;IAaZ;;;;;;;;;;;OAWG;IACH,mBAAmB,CAAC,KAAK,EAAE,GAAG,CAAC,KAAK,GAAG,MAAM;IAI7C;;;;;;OAMG;IACH,gBAAgB,CAAC,KAAK,EAAE,GAAG,CAAC,KAAK,GAAG,MAAM;IAI1C;;;;OAIG;IACH,oBAAoB,CAAC,KAAK,EAAE,GAAG,CAAC,KAAK,GAAG,GAAG,CAAC,eAAe,EAAE;CAY9D;AAED,wBAAgB,mBAAmB,CACjC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAChC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,MAAM,CAAC,KAAK,EAAE,MAAM,CAAC,EACxD,MAAM,EAAE,mBAAmB,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,aAAa,CAAC,CAAC,EAAE,CAAC,CAAC,CAExD"}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Configuration for a {@link ParamRegistry}.
|
|
3
|
+
*/
|
|
4
|
+
export interface ParamRegistryConfig<P extends Record<string, string> = Record<string, string>, S extends Record<string, string> = Record<never, string>> {
|
|
5
|
+
/**
|
|
6
|
+
* Path prefix shared by every parameter and secret in this registry.
|
|
7
|
+
*
|
|
8
|
+
* Must start with `/` and contain at least one path segment, so
|
|
9
|
+
* `/team/my-app` is valid while `team/my-app` throws. Trailing slashes are
|
|
10
|
+
* ignored.
|
|
11
|
+
*/
|
|
12
|
+
readonly prefix: string;
|
|
13
|
+
/**
|
|
14
|
+
* Maps a logical key to the final path segment of an SSM parameter.
|
|
15
|
+
*
|
|
16
|
+
* These keys become the only values accepted by the `key` argument of
|
|
17
|
+
* {@link ParamRegistry.putParameter}, {@link ParamRegistry.parameterRef}
|
|
18
|
+
* and {@link ParamRegistry.secureParameterRef}, so an unknown key is a
|
|
19
|
+
* compile error.
|
|
20
|
+
*/
|
|
21
|
+
readonly parameters: P;
|
|
22
|
+
/**
|
|
23
|
+
* Maps a logical key to the final path segment of a Secrets Manager secret.
|
|
24
|
+
*
|
|
25
|
+
* Omit this entirely if the registry has no secrets, which also makes
|
|
26
|
+
* {@link ParamRegistry.createSecret} uncallable.
|
|
27
|
+
*/
|
|
28
|
+
readonly secrets?: S;
|
|
29
|
+
/**
|
|
30
|
+
* Emit a `CfnOutput` containing the raw value for every `putParameter`
|
|
31
|
+
* call.
|
|
32
|
+
*
|
|
33
|
+
* Defaults to `false`. When enabled the value is rendered into the stack
|
|
34
|
+
* outputs, where anyone with stack read access can see it, so only turn
|
|
35
|
+
* this on for non-sensitive values you actually want surfaced.
|
|
36
|
+
*/
|
|
37
|
+
readonly emitOutputs?: boolean;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Options accepted by {@link ParamRegistry.parameterRef} and
|
|
41
|
+
* {@link ParamRegistry.secureParameterRef}.
|
|
42
|
+
*/
|
|
43
|
+
export interface DynamicRefOptions {
|
|
44
|
+
/**
|
|
45
|
+
* Pin the dynamic reference to a specific parameter version.
|
|
46
|
+
*
|
|
47
|
+
* Without this, CloudFormation resolves whatever version is current at
|
|
48
|
+
* deploy time and never reports drift when the value changes.
|
|
49
|
+
*/
|
|
50
|
+
readonly version?: number;
|
|
51
|
+
}
|
|
52
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,WAAW,mBAAmB,CAClC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EACzD,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,MAAM,CAAC,KAAK,EAAE,MAAM,CAAC;IAExD;;;;;;OAMG;IACH,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IAExB;;;;;;;OAOG;IACH,QAAQ,CAAC,UAAU,EAAE,CAAC,CAAC;IAEvB;;;;;OAKG;IACH,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;IAErB;;;;;;;OAOG;IACH,QAAQ,CAAC,WAAW,CAAC,EAAE,OAAO,CAAC;CAChC;AAED;;;GAGG;AACH,MAAM,WAAW,iBAAiB;IAChC;;;;;OAKG;IACH,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;CAC3B"}
|
package/package.json
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "cdk-ssm-refs",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Typed registry for SSM parameters and Secrets Manager secrets in AWS CDK.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"sideEffects": false,
|
|
7
|
+
"engines": {
|
|
8
|
+
"node": ">=20.19.0"
|
|
9
|
+
},
|
|
10
|
+
"scripts": {
|
|
11
|
+
"build": "bun build ./src/index.ts --outdir ./dist --target node --format esm --packages external && tsc -p tsconfig.build.json",
|
|
12
|
+
"typecheck": "tsc --noEmit",
|
|
13
|
+
"test": "vitest run",
|
|
14
|
+
"prepack": "bun run build"
|
|
15
|
+
},
|
|
16
|
+
"main": "./dist/index.js",
|
|
17
|
+
"types": "./dist/index.d.ts",
|
|
18
|
+
"exports": {
|
|
19
|
+
".": {
|
|
20
|
+
"types": "./dist/index.d.ts",
|
|
21
|
+
"default": "./dist/index.js"
|
|
22
|
+
},
|
|
23
|
+
"./package.json": "./package.json"
|
|
24
|
+
},
|
|
25
|
+
"files": [
|
|
26
|
+
"dist"
|
|
27
|
+
],
|
|
28
|
+
"devDependencies": {
|
|
29
|
+
"@types/bun": "latest",
|
|
30
|
+
"aws-cdk-lib": "^2.271.0",
|
|
31
|
+
"constructs": "^10.8.1",
|
|
32
|
+
"prettier": "^3.9.9",
|
|
33
|
+
"vitest": "^5.0.2"
|
|
34
|
+
},
|
|
35
|
+
"peerDependencies": {
|
|
36
|
+
"typescript": "^5.9.3",
|
|
37
|
+
"aws-cdk-lib": "^2.271.0",
|
|
38
|
+
"constructs": "^10.8.1"
|
|
39
|
+
}
|
|
40
|
+
}
|