@aws-blocks/bb-app-setting 0.2.0 → 0.3.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/dist/index.aws.d.ts +1 -0
- package/dist/index.aws.d.ts.map +1 -1
- package/dist/index.aws.js +1 -0
- package/dist/index.browser.d.ts +1 -0
- package/dist/index.browser.d.ts.map +1 -1
- package/dist/index.browser.js +1 -0
- package/dist/index.cdk.d.ts +3 -2
- package/dist/index.cdk.d.ts.map +1 -1
- package/dist/index.cdk.js +16 -6
- package/dist/index.cdk.test.js +13 -3
- package/dist/index.mock.d.ts +1 -0
- package/dist/index.mock.d.ts.map +1 -1
- package/dist/index.mock.js +1 -0
- package/dist/secrets-bulk.d.ts +15 -0
- package/dist/secrets-bulk.d.ts.map +1 -0
- package/dist/secrets-bulk.js +16 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +10 -3
- package/src/index.aws.ts +1 -0
- package/src/index.browser.ts +2 -0
- package/src/index.cdk.test.ts +14 -3
- package/src/index.cdk.ts +17 -6
- package/src/index.mock.ts +1 -0
- package/src/secrets-bulk.ts +17 -0
- package/src/version.ts +1 -1
package/dist/index.aws.d.ts
CHANGED
|
@@ -4,6 +4,7 @@ import type { AppSettingOptions } from './types.js';
|
|
|
4
4
|
import type { ChildLogger } from '@aws-blocks/bb-logger';
|
|
5
5
|
export { AppSettingErrors } from './errors.js';
|
|
6
6
|
export type { AppSettingOptions } from './types.js';
|
|
7
|
+
export { SECRETS_BULK_CONSTRUCT_ID } from './secrets-bulk.js';
|
|
7
8
|
/**
|
|
8
9
|
* A single application configuration value backed by SSM Parameter Store.
|
|
9
10
|
*
|
package/dist/index.aws.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.aws.d.ts","sourceRoot":"","sources":["../src/index.aws.ts"],"names":[],"mappings":"AAQA,OAAO,EAAE,KAAK,EAA6C,MAAM,kBAAkB,CAAC;AACpF,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAGpD,OAAO,KAAK,EAAE,iBAAiB,EAA6B,MAAM,YAAY,CAAC;AAG/E,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AAGzD,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAC/C,YAAY,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"index.aws.d.ts","sourceRoot":"","sources":["../src/index.aws.ts"],"names":[],"mappings":"AAQA,OAAO,EAAE,KAAK,EAA6C,MAAM,kBAAkB,CAAC;AACpF,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAGpD,OAAO,KAAK,EAAE,iBAAiB,EAA6B,MAAM,YAAY,CAAC;AAG/E,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AAGzD,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAC/C,YAAY,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AACpD,OAAO,EAAE,yBAAyB,EAAE,MAAM,mBAAmB,CAAC;AAqB9D;;;;;;;;;;;;;;;;;GAiBG;AACH,qBAAa,UAAU,CAAC,CAAC,GAAG,MAAM,CAAE,SAAQ,KAAK;IAChD;;;;;;OAMG;IACH,MAAM,CAAC,YAAY,CAAC,CAAC,GAAG,MAAM,EAC7B,KAAK,EAAE,WAAW,EAClB,EAAE,EAAE,MAAM,EACV,OAAO,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,OAAO,CAAC;QAAC,SAAS,CAAC,EAAE,MAAM,CAAA;KAAE,GAC7D,UAAU,CAAC,CAAC,CAAC;IAKhB,QAAQ,CAAC,MAAM,gBAAW;IAC1B,OAAO,CAAC,MAAM,CAAC,CAAsB;IACrC,OAAO,CAAC,QAAQ,CAAU;IAC1B,OAAO,CAAC,SAAS,CAAC,CAAS;IAC3B,OAAO,CAAC,MAAM,CAAY;IAE1B,2FAA2F;IAC3F,SAAS,CAAC,GAAG,EAAE,WAAW,CAAC;gBAEf,KAAK,EAAE,WAAW,EAAE,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,iBAAiB,CAAC,CAAC,CAAC;IAczE;;;;;;;;;;;;;OAaG;IACG,GAAG,IAAI,OAAO,CAAC,CAAC,CAAC;IA2BvB;;;;;;;;;;;;;;OAcG;IACG,GAAG,CAAC,KAAK,EAAE,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;CAelC"}
|
package/dist/index.aws.js
CHANGED
|
@@ -7,6 +7,7 @@ import { BB_NAME, BB_VERSION } from './version.js';
|
|
|
7
7
|
import { Logger } from '@aws-blocks/bb-logger';
|
|
8
8
|
// Re-export public types from types module (canonical source)
|
|
9
9
|
export { AppSettingErrors } from './errors.js';
|
|
10
|
+
export { SECRETS_BULK_CONSTRUCT_ID } from './secrets-bulk.js';
|
|
10
11
|
// ── Helpers ─────────────────────────────────────────────────────────────────
|
|
11
12
|
function blocksError(name, message) {
|
|
12
13
|
const err = new Error(`${name}: ${message}`);
|
package/dist/index.browser.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.browser.d.ts","sourceRoot":"","sources":["../src/index.browser.ts"],"names":[],"mappings":"AAIA,qBAAa,UAAU;IACtB,MAAM,CAAC,YAAY,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,GAAG,GAAG;gBAG5B,GAAG,IAAI,EAAE,GAAG,EAAE;CAC1B"}
|
|
1
|
+
{"version":3,"file":"index.browser.d.ts","sourceRoot":"","sources":["../src/index.browser.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,yBAAyB,EAAE,MAAM,mBAAmB,CAAC;AAE9D,qBAAa,UAAU;IACtB,MAAM,CAAC,YAAY,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,GAAG,GAAG;gBAG5B,GAAG,IAAI,EAAE,GAAG,EAAE;CAC1B"}
|
package/dist/index.browser.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
|
2
2
|
// SPDX-License-Identifier: Apache-2.0
|
|
3
3
|
// Browser stub - AppSetting runs server-side only
|
|
4
|
+
export { SECRETS_BULK_CONSTRUCT_ID } from './secrets-bulk.js';
|
|
4
5
|
export class AppSetting {
|
|
5
6
|
static fromExisting(...args) {
|
|
6
7
|
return new AppSetting();
|
package/dist/index.cdk.d.ts
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { BuildingBlockScope } from '@aws-blocks/core/cdk';
|
|
2
2
|
import type { ScopeParent } from '@aws-blocks/core';
|
|
3
3
|
import type { AppSettingOptions } from './types.js';
|
|
4
4
|
export { AppSettingErrors } from './errors.js';
|
|
5
5
|
export type { AppSettingOptions } from './types.js';
|
|
6
|
+
export { SECRETS_BULK_CONSTRUCT_ID } from './secrets-bulk.js';
|
|
6
7
|
/**
|
|
7
8
|
* CDK construct for AppSetting. Creates a single SSM parameter (String or
|
|
8
9
|
* SecureString) and grants the shared Lambda handler read/write permissions.
|
|
@@ -14,7 +15,7 @@ export type { AppSettingOptions } from './types.js';
|
|
|
14
15
|
* or with a customer-managed key when `kmsKeyArn` is provided (the handler is
|
|
15
16
|
* then granted `kms:Decrypt`/`Encrypt` on that specific key ARN).
|
|
16
17
|
*/
|
|
17
|
-
export declare class AppSetting<T = string> extends
|
|
18
|
+
export declare class AppSetting<T = string> extends BuildingBlockScope {
|
|
18
19
|
/**
|
|
19
20
|
* Reference an SSM parameter that is created and owned **outside this stack**
|
|
20
21
|
* (e.g. a connection string seeded by `ensureSecrets` before deploy). The
|
package/dist/index.cdk.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cdk.d.ts","sourceRoot":"","sources":["../src/index.cdk.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.cdk.d.ts","sourceRoot":"","sources":["../src/index.cdk.ts"],"names":[],"mappings":"AAUA,OAAO,EAAE,kBAAkB,EAAwC,MAAM,sBAAsB,CAAC;AAChG,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAEpD,OAAO,KAAK,EAAE,iBAAiB,EAA6B,MAAM,YAAY,CAAC;AAE/E,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAC/C,YAAY,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AACpD,OAAO,EAAE,yBAAyB,EAAE,MAAM,mBAAmB,CAAC;AAI9D;;;;;;;;;;GAUG;AACH,qBAAa,UAAU,CAAC,CAAC,GAAG,MAAM,CAAE,SAAQ,kBAAkB;IAC7D;;;;;;;;;;;;OAYG;IACH,MAAM,CAAC,YAAY,CAAC,CAAC,GAAG,MAAM,EAC7B,KAAK,EAAE,WAAW,EAClB,EAAE,EAAE,MAAM,EACV,OAAO,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,OAAO,CAAC;QAAC,SAAS,CAAC,EAAE,MAAM,CAAA;KAAE,GAC7D,UAAU,CAAC,CAAC,CAAC;gBAKJ,KAAK,EAAE,WAAW,EAAE,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,iBAAiB,CAAC,CAAC,CAAC;CAyJzE"}
|
package/dist/index.cdk.js
CHANGED
|
@@ -4,10 +4,14 @@ import * as cdk from 'aws-cdk-lib';
|
|
|
4
4
|
import * as ssm from 'aws-cdk-lib/aws-ssm';
|
|
5
5
|
import * as iam from 'aws-cdk-lib/aws-iam';
|
|
6
6
|
import * as lambda from 'aws-cdk-lib/aws-lambda';
|
|
7
|
+
import { LogGroup } from 'aws-cdk-lib/aws-logs';
|
|
7
8
|
import * as cr from 'aws-cdk-lib/custom-resources';
|
|
8
|
-
import
|
|
9
|
+
import * as ec2 from 'aws-cdk-lib/aws-ec2';
|
|
10
|
+
import { BuildingBlockScope, registerConfig, DEFAULT_NODE_RUNTIME } from '@aws-blocks/core/cdk';
|
|
9
11
|
import { AppSettingErrors } from './errors.js';
|
|
10
12
|
export { AppSettingErrors } from './errors.js';
|
|
13
|
+
export { SECRETS_BULK_CONSTRUCT_ID } from './secrets-bulk.js';
|
|
14
|
+
import { SECRETS_BULK_CONSTRUCT_ID } from './secrets-bulk.js';
|
|
11
15
|
/**
|
|
12
16
|
* CDK construct for AppSetting. Creates a single SSM parameter (String or
|
|
13
17
|
* SecureString) and grants the shared Lambda handler read/write permissions.
|
|
@@ -19,7 +23,7 @@ export { AppSettingErrors } from './errors.js';
|
|
|
19
23
|
* or with a customer-managed key when `kmsKeyArn` is provided (the handler is
|
|
20
24
|
* then granted `kms:Decrypt`/`Encrypt` on that specific key ARN).
|
|
21
25
|
*/
|
|
22
|
-
export class AppSetting extends
|
|
26
|
+
export class AppSetting extends BuildingBlockScope {
|
|
23
27
|
/**
|
|
24
28
|
* Reference an SSM parameter that is created and owned **outside this stack**
|
|
25
29
|
* (e.g. a connection string seeded by `ensureSecrets` before deploy). The
|
|
@@ -38,7 +42,7 @@ export class AppSetting extends Scope {
|
|
|
38
42
|
return new AppSetting(scope, id, opts);
|
|
39
43
|
}
|
|
40
44
|
constructor(scope, id, options) {
|
|
41
|
-
super(id, { parent: scope });
|
|
45
|
+
super(id, { parent: scope, vpc: { interfaceEndpoints: [ec2.InterfaceVpcEndpointAwsService.SSM] } });
|
|
42
46
|
// `external` is package-internal (set only by fromExisting), not on the
|
|
43
47
|
// public AppSettingOptions — read it via the internal options type.
|
|
44
48
|
const external = options.external ?? false;
|
|
@@ -112,7 +116,7 @@ export class AppSetting extends Scope {
|
|
|
112
116
|
// then fail tagging it (AddTagsToResource needs ssm:GetParameters).
|
|
113
117
|
// We only need runtime read access, granted below.
|
|
114
118
|
if (!external) {
|
|
115
|
-
registerSecret(cdk.Stack.of(this), parameterName, options.kmsKeyArn);
|
|
119
|
+
registerSecret(cdk.Stack.of(this), parameterName, this.defaults.logRetention, options.kmsKeyArn);
|
|
116
120
|
}
|
|
117
121
|
// Grant the handler KMS access. External secrets are read-only (Decrypt
|
|
118
122
|
// only); stack-managed secrets also need Encrypt so the app can write the
|
|
@@ -171,7 +175,7 @@ const SECRET_BULK_KEY = Symbol.for('BLOCKS_SECRET_BULK_INIT');
|
|
|
171
175
|
* All subsequent calls just append to the parameter list (resolved lazily at
|
|
172
176
|
* synth time).
|
|
173
177
|
*/
|
|
174
|
-
function registerSecret(stack, parameterName, keyId) {
|
|
178
|
+
function registerSecret(stack, parameterName, logRetention, keyId) {
|
|
175
179
|
let state = stack[SECRET_BULK_KEY];
|
|
176
180
|
if (state) {
|
|
177
181
|
state.params.push({ name: parameterName, keyId });
|
|
@@ -183,6 +187,12 @@ function registerSecret(stack, parameterName, keyId) {
|
|
|
183
187
|
const secretInitFn = new lambda.Function(stack, 'BlocksSecretInitFn', {
|
|
184
188
|
runtime: DEFAULT_NODE_RUNTIME,
|
|
185
189
|
handler: 'index.handler',
|
|
190
|
+
// Own the log group so its retention follows the stack-wide default
|
|
191
|
+
// instead of AWS's infinite retention.
|
|
192
|
+
logGroup: new LogGroup(stack, 'BlocksSecretInitLogs', {
|
|
193
|
+
retention: logRetention,
|
|
194
|
+
removalPolicy: cdk.RemovalPolicy.DESTROY,
|
|
195
|
+
}),
|
|
186
196
|
code: lambda.Code.fromInline(`
|
|
187
197
|
const { SSMClient, GetParameterCommand, PutParameterCommand, DeleteParameterCommand, AddTagsToResourceCommand } = require('@aws-sdk/client-ssm');
|
|
188
198
|
const crypto = require('crypto');
|
|
@@ -280,7 +290,7 @@ function registerSecret(stack, parameterName, keyId) {
|
|
|
280
290
|
const provider = new cr.Provider(stack, 'BlocksSecretProvider', {
|
|
281
291
|
onEventHandler: secretInitFn,
|
|
282
292
|
});
|
|
283
|
-
new cdk.CustomResource(stack,
|
|
293
|
+
new cdk.CustomResource(stack, SECRETS_BULK_CONSTRUCT_ID, {
|
|
284
294
|
serviceToken: provider.serviceToken,
|
|
285
295
|
properties: {
|
|
286
296
|
Parameters: cdk.Lazy.any({ produce: () => state.params }),
|
package/dist/index.cdk.test.js
CHANGED
|
@@ -10,12 +10,13 @@ import { test } from 'node:test';
|
|
|
10
10
|
import assert from 'node:assert';
|
|
11
11
|
import * as cdk from 'aws-cdk-lib';
|
|
12
12
|
import { Template, Match } from 'aws-cdk-lib/assertions';
|
|
13
|
-
import { Scope, DEFAULT_NODE_RUNTIME } from '@aws-blocks/core/cdk';
|
|
13
|
+
import { Scope, DEFAULT_NODE_RUNTIME, BlocksPresets } from '@aws-blocks/core/cdk';
|
|
14
14
|
import { AppSetting } from './index.cdk.js';
|
|
15
15
|
class StubBlocksStack extends cdk.Stack {
|
|
16
16
|
handler;
|
|
17
17
|
executionRole;
|
|
18
18
|
id;
|
|
19
|
+
defaults = BlocksPresets.production;
|
|
19
20
|
constructor(scope, id) {
|
|
20
21
|
super(scope, id);
|
|
21
22
|
this.id = id;
|
|
@@ -31,12 +32,21 @@ class StubBlocksStack extends cdk.Stack {
|
|
|
31
32
|
});
|
|
32
33
|
}
|
|
33
34
|
}
|
|
34
|
-
function setup() {
|
|
35
|
+
function setup(defaults = BlocksPresets.production, stackId = 'TestStack') {
|
|
35
36
|
const app = new cdk.App();
|
|
36
|
-
const stack = new StubBlocksStack(app,
|
|
37
|
+
const stack = new StubBlocksStack(app, stackId);
|
|
38
|
+
stack.defaults = defaults;
|
|
37
39
|
const parent = new Scope('app');
|
|
38
40
|
return { stack, parent };
|
|
39
41
|
}
|
|
42
|
+
test('CDK: the secret-init Lambda log group adopts defaults.logRetention', () => {
|
|
43
|
+
const { stack, parent } = setup(BlocksPresets.sandbox, 'SecretRetentionStack');
|
|
44
|
+
new AppSetting(parent, 'my-secret', { secret: true, name: '/myapp/secret-key' });
|
|
45
|
+
const template = Template.fromStack(stack);
|
|
46
|
+
// The secret-init Lambda now owns an explicit log group whose retention
|
|
47
|
+
// follows the stack-wide default (sandbox → one week) instead of infinite.
|
|
48
|
+
template.hasResourceProperties('AWS::Logs::LogGroup', { RetentionInDays: 7 });
|
|
49
|
+
});
|
|
40
50
|
test('CDK: secret AppSetting SSM policy is scoped to specific parameter ARN (not wildcard)', () => {
|
|
41
51
|
const { stack, parent } = setup();
|
|
42
52
|
new AppSetting(parent, 'my-secret', { secret: true, name: '/myapp/secret-key' });
|
package/dist/index.mock.d.ts
CHANGED
|
@@ -4,6 +4,7 @@ import type { AppSettingOptions } from './types.js';
|
|
|
4
4
|
import type { ChildLogger } from '@aws-blocks/bb-logger';
|
|
5
5
|
export { AppSettingErrors } from './errors.js';
|
|
6
6
|
export type { AppSettingOptions } from './types.js';
|
|
7
|
+
export { SECRETS_BULK_CONSTRUCT_ID } from './secrets-bulk.js';
|
|
7
8
|
/**
|
|
8
9
|
* A single application configuration value backed by SSM Parameter Store.
|
|
9
10
|
*
|
package/dist/index.mock.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mock.d.ts","sourceRoot":"","sources":["../src/index.mock.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,KAAK,EAA0B,MAAM,kBAAkB,CAAC;AAEjE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAMpD,OAAO,KAAK,EAAE,iBAAiB,EAA6B,MAAM,YAAY,CAAC;AAE/E,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AAIzD,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAC/C,YAAY,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"index.mock.d.ts","sourceRoot":"","sources":["../src/index.mock.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,KAAK,EAA0B,MAAM,kBAAkB,CAAC;AAEjE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAMpD,OAAO,KAAK,EAAE,iBAAiB,EAA6B,MAAM,YAAY,CAAC;AAE/E,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AAIzD,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAC/C,YAAY,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AACpD,OAAO,EAAE,yBAAyB,EAAE,MAAM,mBAAmB,CAAC;AAsC9D;;;;;;;;;;;;;;;;;GAiBG;AACH,qBAAa,UAAU,CAAC,CAAC,GAAG,MAAM,CAAE,SAAQ,KAAK;IAChD;;;;;;;;;OASG;IACH,MAAM,CAAC,YAAY,CAAC,CAAC,GAAG,MAAM,EAC7B,KAAK,EAAE,WAAW,EAClB,EAAE,EAAE,MAAM,EACV,OAAO,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,OAAO,CAAC;QAAC,SAAS,CAAC,EAAE,MAAM,CAAA;KAAE,GAC7D,UAAU,CAAC,CAAC,CAAC;IAKhB,OAAO,CAAC,aAAa,CAAS;IAC9B,OAAO,CAAC,YAAY,CAAI;IACxB,OAAO,CAAC,MAAM,CAAC,CAAsB;IACrC,OAAO,CAAC,QAAQ,CAAU;IAE1B,2FAA2F;IAC3F,SAAS,CAAC,GAAG,EAAE,WAAW,CAAC;gBAEf,KAAK,EAAE,WAAW,EAAE,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,iBAAiB,CAAC,CAAC,CAAC;IAyBzE;;;;;;;;;;;OAWG;IACG,GAAG,IAAI,OAAO,CAAC,CAAC,CAAC;IAWvB;;;;;;;;;;;;;;OAcG;IACG,GAAG,CAAC,KAAK,EAAE,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;CAelC"}
|
package/dist/index.mock.js
CHANGED
|
@@ -10,6 +10,7 @@ import { Logger } from '@aws-blocks/bb-logger';
|
|
|
10
10
|
import { BB_NAME, BB_VERSION } from './version.js';
|
|
11
11
|
// Re-export public types
|
|
12
12
|
export { AppSettingErrors } from './errors.js';
|
|
13
|
+
export { SECRETS_BULK_CONSTRUCT_ID } from './secrets-bulk.js';
|
|
13
14
|
// ── Helpers ─────────────────────────────────────────────────────────────────
|
|
14
15
|
const MAX_VALUE_BYTES = 4096; // SSM standard-tier 4 KB limit
|
|
15
16
|
function readSettings(scope) {
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Construct id of the shared, per-stack custom resource that writes every
|
|
3
|
+
* `secret: true` `AppSetting`'s SecureString value at deploy time (created
|
|
4
|
+
* lazily by the first secret AppSetting; a direct child of the stack).
|
|
5
|
+
*
|
|
6
|
+
* Exported so sibling blocks that must order a resource *after* the secret
|
|
7
|
+
* values are written — e.g. `bb-auth-oidc`'s IdP-registration custom resource —
|
|
8
|
+
* can locate it via `Stack.of(x).node.tryFindChild(SECRETS_BULK_CONSTRUCT_ID)`
|
|
9
|
+
* without hard-coding the string. This is the single source of truth for the id:
|
|
10
|
+
* the CDK layer creates the construct with it, so a rename here moves both the
|
|
11
|
+
* producer and every consumer together and the cross-package coupling can't
|
|
12
|
+
* drift silently.
|
|
13
|
+
*/
|
|
14
|
+
export declare const SECRETS_BULK_CONSTRUCT_ID = "BlocksSecretsBulk";
|
|
15
|
+
//# sourceMappingURL=secrets-bulk.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"secrets-bulk.d.ts","sourceRoot":"","sources":["../src/secrets-bulk.ts"],"names":[],"mappings":"AAGA;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,yBAAyB,sBAAsB,CAAC"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
/**
|
|
4
|
+
* Construct id of the shared, per-stack custom resource that writes every
|
|
5
|
+
* `secret: true` `AppSetting`'s SecureString value at deploy time (created
|
|
6
|
+
* lazily by the first secret AppSetting; a direct child of the stack).
|
|
7
|
+
*
|
|
8
|
+
* Exported so sibling blocks that must order a resource *after* the secret
|
|
9
|
+
* values are written — e.g. `bb-auth-oidc`'s IdP-registration custom resource —
|
|
10
|
+
* can locate it via `Stack.of(x).node.tryFindChild(SECRETS_BULK_CONSTRUCT_ID)`
|
|
11
|
+
* without hard-coding the string. This is the single source of truth for the id:
|
|
12
|
+
* the CDK layer creates the construct with it, so a rename here moves both the
|
|
13
|
+
* producer and every consumer together and the cross-package coupling can't
|
|
14
|
+
* drift silently.
|
|
15
|
+
*/
|
|
16
|
+
export const SECRETS_BULK_CONSTRUCT_ID = 'BlocksSecretsBulk';
|
package/dist/version.d.ts
CHANGED
package/dist/version.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aws-blocks/bb-app-setting",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
|
+
"keywords": [
|
|
5
|
+
"aws-blocks",
|
|
6
|
+
"configuration",
|
|
7
|
+
"ssm",
|
|
8
|
+
"parameter-store",
|
|
9
|
+
"secrets"
|
|
10
|
+
],
|
|
4
11
|
"repository": {
|
|
5
12
|
"type": "git",
|
|
6
13
|
"url": "git+https://github.com/aws-devtools-labs/aws-blocks.git",
|
|
@@ -39,8 +46,8 @@
|
|
|
39
46
|
"test": "node --test --test-concurrency=1 dist/**/*.test.js"
|
|
40
47
|
},
|
|
41
48
|
"dependencies": {
|
|
42
|
-
"@aws-blocks/core": "^0.
|
|
43
|
-
"@aws-blocks/bb-logger": "^0.
|
|
49
|
+
"@aws-blocks/core": "^0.5.0",
|
|
50
|
+
"@aws-blocks/bb-logger": "^0.2.0",
|
|
44
51
|
"@aws-sdk/client-ssm": "^3.0.0",
|
|
45
52
|
"@standard-schema/spec": "^1.1.0"
|
|
46
53
|
},
|
package/src/index.aws.ts
CHANGED
|
@@ -18,6 +18,7 @@ import type { ChildLogger } from '@aws-blocks/bb-logger';
|
|
|
18
18
|
// Re-export public types from types module (canonical source)
|
|
19
19
|
export { AppSettingErrors } from './errors.js';
|
|
20
20
|
export type { AppSettingOptions } from './types.js';
|
|
21
|
+
export { SECRETS_BULK_CONSTRUCT_ID } from './secrets-bulk.js';
|
|
21
22
|
|
|
22
23
|
// ── Helpers ─────────────────────────────────────────────────────────────────
|
|
23
24
|
|
package/src/index.browser.ts
CHANGED
package/src/index.cdk.test.ts
CHANGED
|
@@ -12,13 +12,14 @@ import assert from 'node:assert';
|
|
|
12
12
|
import * as cdk from 'aws-cdk-lib';
|
|
13
13
|
import type { Construct } from 'constructs';
|
|
14
14
|
import { Template, Match } from 'aws-cdk-lib/assertions';
|
|
15
|
-
import { Scope, DEFAULT_NODE_RUNTIME } from '@aws-blocks/core/cdk';
|
|
15
|
+
import { Scope, DEFAULT_NODE_RUNTIME, BlocksPresets, type BlocksDefaults } from '@aws-blocks/core/cdk';
|
|
16
16
|
import { AppSetting } from './index.cdk.js';
|
|
17
17
|
|
|
18
18
|
class StubBlocksStack extends cdk.Stack {
|
|
19
19
|
public readonly handler: cdk.aws_lambda.Function;
|
|
20
20
|
public readonly executionRole: cdk.aws_iam.IRole;
|
|
21
21
|
public readonly id: string;
|
|
22
|
+
public defaults: BlocksDefaults = BlocksPresets.production;
|
|
22
23
|
constructor(scope: Construct, id: string) {
|
|
23
24
|
super(scope, id);
|
|
24
25
|
this.id = id;
|
|
@@ -35,13 +36,23 @@ class StubBlocksStack extends cdk.Stack {
|
|
|
35
36
|
}
|
|
36
37
|
}
|
|
37
38
|
|
|
38
|
-
function setup(): { stack: StubBlocksStack; parent: Scope } {
|
|
39
|
+
function setup(defaults: BlocksDefaults = BlocksPresets.production, stackId = 'TestStack'): { stack: StubBlocksStack; parent: Scope } {
|
|
39
40
|
const app = new cdk.App();
|
|
40
|
-
const stack = new StubBlocksStack(app,
|
|
41
|
+
const stack = new StubBlocksStack(app, stackId);
|
|
42
|
+
stack.defaults = defaults;
|
|
41
43
|
const parent = new Scope('app');
|
|
42
44
|
return { stack, parent };
|
|
43
45
|
}
|
|
44
46
|
|
|
47
|
+
test('CDK: the secret-init Lambda log group adopts defaults.logRetention', () => {
|
|
48
|
+
const { stack, parent } = setup(BlocksPresets.sandbox, 'SecretRetentionStack');
|
|
49
|
+
new AppSetting(parent, 'my-secret', { secret: true, name: '/myapp/secret-key' });
|
|
50
|
+
const template = Template.fromStack(stack);
|
|
51
|
+
// The secret-init Lambda now owns an explicit log group whose retention
|
|
52
|
+
// follows the stack-wide default (sandbox → one week) instead of infinite.
|
|
53
|
+
template.hasResourceProperties('AWS::Logs::LogGroup', { RetentionInDays: 7 });
|
|
54
|
+
});
|
|
55
|
+
|
|
45
56
|
test('CDK: secret AppSetting SSM policy is scoped to specific parameter ARN (not wildcard)', () => {
|
|
46
57
|
const { stack, parent } = setup();
|
|
47
58
|
new AppSetting(parent, 'my-secret', { secret: true, name: '/myapp/secret-key' });
|
package/src/index.cdk.ts
CHANGED
|
@@ -5,14 +5,19 @@ import * as cdk from 'aws-cdk-lib';
|
|
|
5
5
|
import * as ssm from 'aws-cdk-lib/aws-ssm';
|
|
6
6
|
import * as iam from 'aws-cdk-lib/aws-iam';
|
|
7
7
|
import * as lambda from 'aws-cdk-lib/aws-lambda';
|
|
8
|
+
import { LogGroup, type RetentionDays } from 'aws-cdk-lib/aws-logs';
|
|
8
9
|
import * as cr from 'aws-cdk-lib/custom-resources';
|
|
9
|
-
import
|
|
10
|
+
import * as ec2 from 'aws-cdk-lib/aws-ec2';
|
|
11
|
+
import { BuildingBlockScope, registerConfig, DEFAULT_NODE_RUNTIME } from '@aws-blocks/core/cdk';
|
|
10
12
|
import type { ScopeParent } from '@aws-blocks/core';
|
|
11
13
|
import { AppSettingErrors } from './errors.js';
|
|
12
14
|
import type { AppSettingOptions, InternalAppSettingOptions } from './types.js';
|
|
13
15
|
|
|
14
16
|
export { AppSettingErrors } from './errors.js';
|
|
15
17
|
export type { AppSettingOptions } from './types.js';
|
|
18
|
+
export { SECRETS_BULK_CONSTRUCT_ID } from './secrets-bulk.js';
|
|
19
|
+
|
|
20
|
+
import { SECRETS_BULK_CONSTRUCT_ID } from './secrets-bulk.js';
|
|
16
21
|
|
|
17
22
|
/**
|
|
18
23
|
* CDK construct for AppSetting. Creates a single SSM parameter (String or
|
|
@@ -25,7 +30,7 @@ export type { AppSettingOptions } from './types.js';
|
|
|
25
30
|
* or with a customer-managed key when `kmsKeyArn` is provided (the handler is
|
|
26
31
|
* then granted `kms:Decrypt`/`Encrypt` on that specific key ARN).
|
|
27
32
|
*/
|
|
28
|
-
export class AppSetting<T = string> extends
|
|
33
|
+
export class AppSetting<T = string> extends BuildingBlockScope {
|
|
29
34
|
/**
|
|
30
35
|
* Reference an SSM parameter that is created and owned **outside this stack**
|
|
31
36
|
* (e.g. a connection string seeded by `ensureSecrets` before deploy). The
|
|
@@ -49,7 +54,7 @@ export class AppSetting<T = string> extends Scope {
|
|
|
49
54
|
}
|
|
50
55
|
|
|
51
56
|
constructor(scope: ScopeParent, id: string, options: AppSettingOptions<T>) {
|
|
52
|
-
super(id, { parent: scope });
|
|
57
|
+
super(id, { parent: scope, vpc: { interfaceEndpoints: [ec2.InterfaceVpcEndpointAwsService.SSM] } });
|
|
53
58
|
|
|
54
59
|
// `external` is package-internal (set only by fromExisting), not on the
|
|
55
60
|
// public AppSettingOptions — read it via the internal options type.
|
|
@@ -151,7 +156,7 @@ export class AppSetting<T = string> extends Scope {
|
|
|
151
156
|
// then fail tagging it (AddTagsToResource needs ssm:GetParameters).
|
|
152
157
|
// We only need runtime read access, granted below.
|
|
153
158
|
if (!external) {
|
|
154
|
-
registerSecret(cdk.Stack.of(this), parameterName, options.kmsKeyArn);
|
|
159
|
+
registerSecret(cdk.Stack.of(this), parameterName, this.defaults.logRetention, options.kmsKeyArn);
|
|
155
160
|
}
|
|
156
161
|
|
|
157
162
|
// Grant the handler KMS access. External secrets are read-only (Decrypt
|
|
@@ -223,7 +228,7 @@ interface SecretBulkState {
|
|
|
223
228
|
* All subsequent calls just append to the parameter list (resolved lazily at
|
|
224
229
|
* synth time).
|
|
225
230
|
*/
|
|
226
|
-
function registerSecret(stack: cdk.Stack, parameterName: string, keyId?: string): void {
|
|
231
|
+
function registerSecret(stack: cdk.Stack, parameterName: string, logRetention: RetentionDays, keyId?: string): void {
|
|
227
232
|
let state = (stack as any)[SECRET_BULK_KEY] as SecretBulkState | undefined;
|
|
228
233
|
if (state) {
|
|
229
234
|
state.params.push({ name: parameterName, keyId });
|
|
@@ -237,6 +242,12 @@ function registerSecret(stack: cdk.Stack, parameterName: string, keyId?: string)
|
|
|
237
242
|
const secretInitFn = new lambda.Function(stack, 'BlocksSecretInitFn', {
|
|
238
243
|
runtime: DEFAULT_NODE_RUNTIME,
|
|
239
244
|
handler: 'index.handler',
|
|
245
|
+
// Own the log group so its retention follows the stack-wide default
|
|
246
|
+
// instead of AWS's infinite retention.
|
|
247
|
+
logGroup: new LogGroup(stack, 'BlocksSecretInitLogs', {
|
|
248
|
+
retention: logRetention,
|
|
249
|
+
removalPolicy: cdk.RemovalPolicy.DESTROY,
|
|
250
|
+
}),
|
|
240
251
|
code: lambda.Code.fromInline(`
|
|
241
252
|
const { SSMClient, GetParameterCommand, PutParameterCommand, DeleteParameterCommand, AddTagsToResourceCommand } = require('@aws-sdk/client-ssm');
|
|
242
253
|
const crypto = require('crypto');
|
|
@@ -340,7 +351,7 @@ function registerSecret(stack: cdk.Stack, parameterName: string, keyId?: string)
|
|
|
340
351
|
onEventHandler: secretInitFn,
|
|
341
352
|
});
|
|
342
353
|
|
|
343
|
-
new cdk.CustomResource(stack,
|
|
354
|
+
new cdk.CustomResource(stack, SECRETS_BULK_CONSTRUCT_ID, {
|
|
344
355
|
serviceToken: provider.serviceToken,
|
|
345
356
|
properties: {
|
|
346
357
|
Parameters: cdk.Lazy.any({ produce: () => state!.params }),
|
package/src/index.mock.ts
CHANGED
|
@@ -17,6 +17,7 @@ import { BB_NAME, BB_VERSION } from './version.js';
|
|
|
17
17
|
// Re-export public types
|
|
18
18
|
export { AppSettingErrors } from './errors.js';
|
|
19
19
|
export type { AppSettingOptions } from './types.js';
|
|
20
|
+
export { SECRETS_BULK_CONSTRUCT_ID } from './secrets-bulk.js';
|
|
20
21
|
|
|
21
22
|
// ── Helpers ─────────────────────────────────────────────────────────────────
|
|
22
23
|
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Construct id of the shared, per-stack custom resource that writes every
|
|
6
|
+
* `secret: true` `AppSetting`'s SecureString value at deploy time (created
|
|
7
|
+
* lazily by the first secret AppSetting; a direct child of the stack).
|
|
8
|
+
*
|
|
9
|
+
* Exported so sibling blocks that must order a resource *after* the secret
|
|
10
|
+
* values are written — e.g. `bb-auth-oidc`'s IdP-registration custom resource —
|
|
11
|
+
* can locate it via `Stack.of(x).node.tryFindChild(SECRETS_BULK_CONSTRUCT_ID)`
|
|
12
|
+
* without hard-coding the string. This is the single source of truth for the id:
|
|
13
|
+
* the CDK layer creates the construct with it, so a rename here moves both the
|
|
14
|
+
* producer and every consumer together and the cross-package coupling can't
|
|
15
|
+
* drift silently.
|
|
16
|
+
*/
|
|
17
|
+
export const SECRETS_BULK_CONSTRUCT_ID = 'BlocksSecretsBulk';
|
package/src/version.ts
CHANGED