@venturekit/infra 0.0.48 → 0.0.51
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/README.md +34 -0
- package/dist/cdk/app-stack.d.ts +58 -5
- package/dist/cdk/app-stack.d.ts.map +1 -1
- package/dist/cdk/app-stack.js +218 -43
- package/dist/cdk/app-stack.js.map +1 -1
- package/dist/cdk/compute-stack.d.ts +3 -0
- package/dist/cdk/compute-stack.d.ts.map +1 -1
- package/dist/cdk/compute-stack.js +16 -1
- package/dist/cdk/compute-stack.js.map +1 -1
- package/dist/cdk/config-stack.d.ts.map +1 -1
- package/dist/cdk/config-stack.js +2 -0
- package/dist/cdk/config-stack.js.map +1 -1
- package/dist/cdk/data-stack.d.ts +15 -1
- package/dist/cdk/data-stack.d.ts.map +1 -1
- package/dist/cdk/data-stack.js +120 -18
- package/dist/cdk/data-stack.js.map +1 -1
- package/dist/cdk/edge-stack.d.ts +2 -0
- package/dist/cdk/edge-stack.d.ts.map +1 -1
- package/dist/cdk/edge-stack.js +15 -0
- package/dist/cdk/edge-stack.js.map +1 -1
- package/dist/cdk/identity-stack.d.ts.map +1 -1
- package/dist/cdk/identity-stack.js +13 -3
- package/dist/cdk/identity-stack.js.map +1 -1
- package/dist/cdk/migration-runner.d.ts +16 -0
- package/dist/cdk/migration-runner.d.ts.map +1 -1
- package/dist/cdk/migration-runner.js +22 -2
- package/dist/cdk/migration-runner.js.map +1 -1
- package/dist/cdk/network-stack.d.ts +8 -0
- package/dist/cdk/network-stack.d.ts.map +1 -1
- package/dist/cdk/network-stack.js +20 -0
- package/dist/cdk/network-stack.js.map +1 -1
- package/dist/cdk/services-stack.d.ts +5 -0
- package/dist/cdk/services-stack.d.ts.map +1 -1
- package/dist/cdk/services-stack.js +30 -5
- package/dist/cdk/services-stack.js.map +1 -1
- package/dist/cdk/shared/admin-routes.d.ts +8 -9
- package/dist/cdk/shared/admin-routes.d.ts.map +1 -1
- package/dist/cdk/shared/admin-routes.js +12 -38
- package/dist/cdk/shared/admin-routes.js.map +1 -1
- package/dist/cdk/shared/internal-routes.d.ts +17 -0
- package/dist/cdk/shared/internal-routes.d.ts.map +1 -0
- package/dist/cdk/shared/internal-routes.js +127 -0
- package/dist/cdk/shared/internal-routes.js.map +1 -0
- package/dist/cdk/shared/lambda-helpers.d.ts +1 -14
- package/dist/cdk/shared/lambda-helpers.d.ts.map +1 -1
- package/dist/cdk/shared/lambda-helpers.js +2 -24
- package/dist/cdk/shared/lambda-helpers.js.map +1 -1
- package/dist/cdk/shared/otel-layer.d.ts +23 -0
- package/dist/cdk/shared/otel-layer.d.ts.map +1 -0
- package/dist/cdk/shared/otel-layer.js +77 -0
- package/dist/cdk/shared/otel-layer.js.map +1 -0
- package/dist/cdk/shared/ssm-keys.d.ts +50 -4
- package/dist/cdk/shared/ssm-keys.d.ts.map +1 -1
- package/dist/cdk/shared/ssm-keys.js +30 -2
- package/dist/cdk/shared/ssm-keys.js.map +1 -1
- package/dist/cdk/shared/websocket-construct.d.ts +8 -0
- package/dist/cdk/shared/websocket-construct.d.ts.map +1 -1
- package/dist/cdk/shared/websocket-construct.js +7 -2
- package/dist/cdk/shared/websocket-construct.js.map +1 -1
- package/dist/cdk/shared-network-stack.d.ts +44 -0
- package/dist/cdk/shared-network-stack.d.ts.map +1 -0
- package/dist/cdk/shared-network-stack.js +61 -0
- package/dist/cdk/shared-network-stack.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/venture.d.ts +8 -1
- package/dist/venture.d.ts.map +1 -1
- package/dist/venture.js +9 -0
- package/dist/venture.js.map +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -294,6 +294,40 @@ Changing `natType` on an existing deployment causes CloudFormation to
|
|
|
294
294
|
replace the NAT resource (brief data-plane outage during deploy). The
|
|
295
295
|
VPC itself is not replaced and all other resources stay put.
|
|
296
296
|
|
|
297
|
+
## OpenTelemetry tracing (`lambda.otel`)
|
|
298
|
+
|
|
299
|
+
Set `lambda.otel` in an environment config to trace every route, queue,
|
|
300
|
+
cron and function Lambda with the AWS Lambda Layer for OpenTelemetry:
|
|
301
|
+
|
|
302
|
+
```typescript
|
|
303
|
+
// config/prod.ts
|
|
304
|
+
export const prod = {
|
|
305
|
+
preset: 'medium',
|
|
306
|
+
lambda: {
|
|
307
|
+
otel: {
|
|
308
|
+
enabled: true,
|
|
309
|
+
// serviceName: 'cms-api', // OTEL_SERVICE_NAME, default `<project>-<stage>`
|
|
310
|
+
// applicationSignals: true, // CloudWatch Application Signals metrics too
|
|
311
|
+
// layerArn: 'arn:aws:lambda:…', // pin a layer version
|
|
312
|
+
},
|
|
313
|
+
},
|
|
314
|
+
};
|
|
315
|
+
```
|
|
316
|
+
|
|
317
|
+
The stack adds AWS's `AWSOpenTelemetryDistroJs` layer for its region (it
|
|
318
|
+
counts toward the 5-layer limit), sets `AWS_LAMBDA_EXEC_WRAPPER=/opt/otel-instrument`,
|
|
319
|
+
`OTEL_SERVICE_NAME` and `OTEL_AWS_APPLICATION_SIGNALS_ENABLED`, and turns
|
|
320
|
+
on X-Ray active tracing. HTTP, AWS SDK and `pg` calls are traced
|
|
321
|
+
automatically. Spans an app opens with the registered tracer go to X-Ray
|
|
322
|
+
too: `otelTracer()` from `@venturekit-pro/ai` gives one, and passing it to
|
|
323
|
+
the LLM gateway, the agent loop or the harness traces model calls, agents,
|
|
324
|
+
tools and graph nodes. With `applicationSignals: true` the execution roles
|
|
325
|
+
get `CloudWatchLambdaApplicationSignalsExecutionRolePolicy`; Application
|
|
326
|
+
Signals must be enabled once in the account (CloudWatch console). The
|
|
327
|
+
built-in layer versions are those AWS published in October 2026. Pin a
|
|
328
|
+
newer one with `layerArn`. A region with no published layer needs
|
|
329
|
+
`layerArn`.
|
|
330
|
+
|
|
297
331
|
## API
|
|
298
332
|
|
|
299
333
|
### `defineVenture(definition)`
|
package/dist/cdk/app-stack.d.ts
CHANGED
|
@@ -87,6 +87,12 @@ export interface AppStackImports {
|
|
|
87
87
|
* functions.
|
|
88
88
|
*/
|
|
89
89
|
vpc?: ec2.IVpc;
|
|
90
|
+
/**
|
|
91
|
+
* The VPC's INTERNAL CLIENTS security group (network tier): every Lambda
|
|
92
|
+
* joins it, which is what admits it to the VPC's internal service ports
|
|
93
|
+
* (`expose: 'vpc'`) — its own venture's and, in a shared VPC, the others'.
|
|
94
|
+
*/
|
|
95
|
+
internalClientsSecurityGroupId?: string;
|
|
90
96
|
/**
|
|
91
97
|
* HttpApi imported from the edge stack via
|
|
92
98
|
* `HttpApi.fromHttpApiAttributes({ httpApiId, apiEndpoint })`.
|
|
@@ -218,7 +224,12 @@ export interface AppStackImports {
|
|
|
218
224
|
host: string;
|
|
219
225
|
port: string;
|
|
220
226
|
name: string;
|
|
227
|
+
/** The secret functions connect with — the APP login when provisioned. */
|
|
221
228
|
secretArn?: string;
|
|
229
|
+
/** The SYSTEM login's secret — only the functions `systemFunctions` selects read it. */
|
|
230
|
+
systemSecretArn?: string;
|
|
231
|
+
/** `databases[].logins.system.functions` patterns. */
|
|
232
|
+
systemFunctions?: string[];
|
|
222
233
|
}>;
|
|
223
234
|
/**
|
|
224
235
|
* Per-database connection params (RDS SG id + numeric port) used
|
|
@@ -362,10 +373,26 @@ export declare class VentureAppStack extends cdk.Stack {
|
|
|
362
373
|
* single-role layout, where the shared role holds the admin grants.
|
|
363
374
|
*/
|
|
364
375
|
readonly authAdminRole?: iam.Role;
|
|
376
|
+
/**
|
|
377
|
+
* Execution roles of the functions that connect as the SYSTEM database
|
|
378
|
+
* login (`databases[].logins.system.functions`): the only roles that may
|
|
379
|
+
* read its secret. Kept apart from the shared role so no app-login
|
|
380
|
+
* function — whatever runs in it — can fetch the system credential.
|
|
381
|
+
*/
|
|
382
|
+
readonly dbSystemRole?: iam.Role;
|
|
383
|
+
readonly authAdminDbSystemRole?: iam.Role;
|
|
384
|
+
/** `logins.system.functions` of the primary database. */
|
|
385
|
+
private readonly systemLoginPatterns;
|
|
386
|
+
/** The primary database's two secrets, when the two logins are provisioned. */
|
|
387
|
+
private readonly dbLoginSecrets?;
|
|
365
388
|
/** `adminRoutes` patterns collected across every auth intent. */
|
|
366
389
|
private readonly adminRoutePatterns;
|
|
390
|
+
/** `auth[].adminRoutes: 'all'` — the explicit legacy layout (admin grants on the shared role). */
|
|
391
|
+
private readonly legacyAdminGrants;
|
|
367
392
|
/** Shared SG when a VPC is imported; undefined for VPC-less projects. */
|
|
368
393
|
readonly lambdaSecurityGroup?: ec2.SecurityGroup;
|
|
394
|
+
/** The VPC's internal clients group (see `AppStackImports.internalClientsSecurityGroupId`). */
|
|
395
|
+
readonly internalClientsSecurityGroup?: ec2.ISecurityGroup;
|
|
369
396
|
/** Internal HMAC signing secret for Lambda-to-Lambda `invoke()`. */
|
|
370
397
|
readonly internalHmacSecret: secretsmanager.Secret;
|
|
371
398
|
/**
|
|
@@ -410,8 +437,10 @@ export declare class VentureAppStack extends cdk.Stack {
|
|
|
410
437
|
private readonly stage;
|
|
411
438
|
/** Ids of the imported RDS databases — logical `DBInstanceIdentifier` alarm dimensions. */
|
|
412
439
|
private readonly rdsDatabaseIds;
|
|
413
|
-
/** `envConfig.lambda.layers
|
|
440
|
+
/** `envConfig.lambda.layers` (+ the OpenTelemetry layer), attached to every route / queue / cron / function Lambda. */
|
|
414
441
|
private readonly lambdaLayers;
|
|
442
|
+
/** X-Ray active tracing: `lambda.tracingEnabled`, or forced on by `lambda.otel`. */
|
|
443
|
+
private readonly tracingActive;
|
|
415
444
|
/**
|
|
416
445
|
* Resolved custom-sender KMS key ARN per declaring auth intent —
|
|
417
446
|
* `imports.customSenderKmsKeyArnsByAuthId` merged with the
|
|
@@ -488,23 +517,47 @@ export declare class VentureAppStack extends cdk.Stack {
|
|
|
488
517
|
* cross-stack pattern works the same as in-stack.
|
|
489
518
|
*/
|
|
490
519
|
private createRouteFunction;
|
|
520
|
+
/** Every Lambda's groups: its own, and the VPC's internal clients group. */
|
|
521
|
+
private get lambdaSecurityGroups();
|
|
491
522
|
/**
|
|
492
523
|
* Every execution role in this stack: the shared role, plus the
|
|
493
524
|
* auth-admin role when one exists. Baseline grants (secrets, S3,
|
|
494
525
|
* SES, rate-limit table, …) are applied to all of them.
|
|
495
526
|
*/
|
|
496
527
|
private get executionRoles();
|
|
528
|
+
/** The roles whose functions connect as the system database login. */
|
|
529
|
+
private get systemLoginRoles();
|
|
530
|
+
/**
|
|
531
|
+
* The role for a function: Cognito admin or not (`auth[].adminRoutes`),
|
|
532
|
+
* system database login or not (`databases[].logins.system.functions`).
|
|
533
|
+
*/
|
|
534
|
+
private roleFor;
|
|
535
|
+
/** The database env a function running as `role` gets on top of the base env. */
|
|
536
|
+
private loginEnvFor;
|
|
497
537
|
/**
|
|
498
|
-
* Roles that receive the Cognito admin grants:
|
|
499
|
-
*
|
|
500
|
-
*
|
|
538
|
+
* Roles that receive the Cognito admin grants: the shared role under
|
|
539
|
+
* the explicit legacy layout (`adminRoutes: 'all'`), the auth-admin role
|
|
540
|
+
* when routes are listed, and none otherwise.
|
|
501
541
|
*/
|
|
502
542
|
private get cognitoAdminRoles();
|
|
503
543
|
/** Apply one grant to every execution role. */
|
|
504
544
|
private grantEachRole;
|
|
545
|
+
/**
|
|
546
|
+
* Caches: the data stack's cache security group admits THIS stack's
|
|
547
|
+
* Lambda security group only, and every Lambda gets the endpoint and
|
|
548
|
+
* its AUTH token's secret (`VENTURE_CACHE_<ID>_HOST` / `_PORT` /
|
|
549
|
+
* `_AUTH_SECRET_ARN`; `cacheConnection(id)` in `@venturekit/runtime`
|
|
550
|
+
* turns them into TLS + AUTH connection options).
|
|
551
|
+
*/
|
|
552
|
+
private wireCaches;
|
|
553
|
+
/**
|
|
554
|
+
* Internal routes (`internal` intent): the pair secrets this venture keeps
|
|
555
|
+
* for its callers, and the rights to call the ventures it calls.
|
|
556
|
+
*/
|
|
557
|
+
private wireInternalRoutes;
|
|
505
558
|
/** The role a route Lambda runs as — auth-admin when its API path matches `adminRoutes`. */
|
|
506
559
|
private roleForRoute;
|
|
507
|
-
/** The role a standalone / queue / schedule Lambda runs as
|
|
560
|
+
/** The role a standalone / queue / schedule Lambda runs as (`fn:<id>`). */
|
|
508
561
|
private roleForFunction;
|
|
509
562
|
/**
|
|
510
563
|
* Build a Lambda function with the standard app-stack properties
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"app-stack.d.ts","sourceRoot":"","sources":["../../src/cdk/app-stack.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiEG;AAKH,OAAO,KAAK,GAAG,MAAM,aAAa,CAAC;AACnC,OAAO,KAAK,GAAG,MAAM,qBAAqB,CAAC;AAC3C,OAAO,KAAK,GAAG,MAAM,qBAAqB,CAAC;AAC3C,OAAO,KAAK,MAAM,MAAM,wBAAwB,CAAC;AAQjD,OAAO,KAAK,YAAY,MAAM,8BAA8B,CAAC;AAM7D,OAAO,KAAK,cAAc,MAAM,gCAAgC,CAAC;AAEjE,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AACvC,OAAO,EAGL,KAAK,SAAS,EACd,KAAK,aAAa,EACnB,MAAM,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"app-stack.d.ts","sourceRoot":"","sources":["../../src/cdk/app-stack.ts"],"names":[],"mappings":"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"}
|
package/dist/cdk/app-stack.js
CHANGED
|
@@ -87,10 +87,13 @@ import * as cloudwatchActions from 'aws-cdk-lib/aws-cloudwatch-actions';
|
|
|
87
87
|
import * as secretsmanager from 'aws-cdk-lib/aws-secretsmanager';
|
|
88
88
|
import * as logs from 'aws-cdk-lib/aws-logs';
|
|
89
89
|
import { DATA_SAFETY_CONFIG, isExternalAuthIntent, } from '@venturekit/core';
|
|
90
|
-
import { collectAdminRoutePatterns, matchesAdminRoute } from './shared/admin-routes.js';
|
|
91
|
-
import {
|
|
90
|
+
import { collectAdminRoutePatterns, matchesAdminRoute, usesLegacyAdminGrants } from './shared/admin-routes.js';
|
|
91
|
+
import { isInternalRoute, matchesFunctionPattern } from '@venturekit/core';
|
|
92
|
+
import { createInternalCallerSecrets, internalCallStatements } from './shared/internal-routes.js';
|
|
93
|
+
import { readSsmString, writeSsm } from './shared/cross-stack-refs.js';
|
|
94
|
+
import { APPLICATION_SIGNALS_POLICY, defaultOtelLayerArn, otelEnvironment } from './shared/otel-layer.js';
|
|
92
95
|
import { grantCustomSenderInvoke, resolveCustomSender } from './shared/custom-sender.js';
|
|
93
|
-
import { networkKeys, edgeKeys, identityKeys, dataKeys } from './shared/ssm-keys.js';
|
|
96
|
+
import { networkKeys, edgeKeys, identityKeys, dataKeys, internalKeys } from './shared/ssm-keys.js';
|
|
94
97
|
import { bundleHandlerCodeWithEsbuild, inlineStubLambdaCode, toLambdaArchitecture, toLambdaRuntime, toLogRetention, clampLambdaFunctionName, uniqueLogGroupName, discoverFunctionFiles, } from './shared/lambda-helpers.js';
|
|
95
98
|
import { discoverRouteFiles, } from './shared/route-discovery.js';
|
|
96
99
|
import { discoverWsRoutes } from './shared/ws-discovery.js';
|
|
@@ -168,10 +171,26 @@ export class VentureAppStack extends cdk.Stack {
|
|
|
168
171
|
* single-role layout, where the shared role holds the admin grants.
|
|
169
172
|
*/
|
|
170
173
|
authAdminRole;
|
|
174
|
+
/**
|
|
175
|
+
* Execution roles of the functions that connect as the SYSTEM database
|
|
176
|
+
* login (`databases[].logins.system.functions`): the only roles that may
|
|
177
|
+
* read its secret. Kept apart from the shared role so no app-login
|
|
178
|
+
* function — whatever runs in it — can fetch the system credential.
|
|
179
|
+
*/
|
|
180
|
+
dbSystemRole;
|
|
181
|
+
authAdminDbSystemRole;
|
|
182
|
+
/** `logins.system.functions` of the primary database. */
|
|
183
|
+
systemLoginPatterns;
|
|
184
|
+
/** The primary database's two secrets, when the two logins are provisioned. */
|
|
185
|
+
dbLoginSecrets;
|
|
171
186
|
/** `adminRoutes` patterns collected across every auth intent. */
|
|
172
187
|
adminRoutePatterns;
|
|
188
|
+
/** `auth[].adminRoutes: 'all'` — the explicit legacy layout (admin grants on the shared role). */
|
|
189
|
+
legacyAdminGrants;
|
|
173
190
|
/** Shared SG when a VPC is imported; undefined for VPC-less projects. */
|
|
174
191
|
lambdaSecurityGroup;
|
|
192
|
+
/** The VPC's internal clients group (see `AppStackImports.internalClientsSecurityGroupId`). */
|
|
193
|
+
internalClientsSecurityGroup;
|
|
175
194
|
/** Internal HMAC signing secret for Lambda-to-Lambda `invoke()`. */
|
|
176
195
|
internalHmacSecret;
|
|
177
196
|
/**
|
|
@@ -216,8 +235,10 @@ export class VentureAppStack extends cdk.Stack {
|
|
|
216
235
|
stage;
|
|
217
236
|
/** Ids of the imported RDS databases — logical `DBInstanceIdentifier` alarm dimensions. */
|
|
218
237
|
rdsDatabaseIds;
|
|
219
|
-
/** `envConfig.lambda.layers
|
|
238
|
+
/** `envConfig.lambda.layers` (+ the OpenTelemetry layer), attached to every route / queue / cron / function Lambda. */
|
|
220
239
|
lambdaLayers;
|
|
240
|
+
/** X-Ray active tracing: `lambda.tracingEnabled`, or forced on by `lambda.otel`. */
|
|
241
|
+
tracingActive;
|
|
221
242
|
/**
|
|
222
243
|
* Resolved custom-sender KMS key ARN per declaring auth intent —
|
|
223
244
|
* `imports.customSenderKmsKeyArnsByAuthId` merged with the
|
|
@@ -301,11 +322,24 @@ export class VentureAppStack extends cdk.Stack {
|
|
|
301
322
|
this.stage = stage;
|
|
302
323
|
this.rdsDatabaseIds = new Set(imports.databaseEndpointsById?.keys() ?? []);
|
|
303
324
|
this.envConfig = envConfig;
|
|
304
|
-
const
|
|
325
|
+
const otel = envConfig.lambda.otel?.enabled ? envConfig.lambda.otel : undefined;
|
|
326
|
+
let otelLayerArn;
|
|
327
|
+
if (otel) {
|
|
328
|
+
const region = cdk.Stack.of(this).region;
|
|
329
|
+
otelLayerArn =
|
|
330
|
+
otel.layerArn ?? (cdk.Token.isUnresolved(region) ? undefined : defaultOtelLayerArn(region));
|
|
331
|
+
if (!otelLayerArn) {
|
|
332
|
+
throw new Error(cdk.Token.isUnresolved(region)
|
|
333
|
+
? `[venturekit] lambda.otel: the stack has no concrete region to pick the AWS OpenTelemetry layer — set lambda.otel.layerArn.`
|
|
334
|
+
: `[venturekit] lambda.otel: AWS publishes no OpenTelemetry layer for '${region}' — set lambda.otel.layerArn.`);
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
this.tracingActive = envConfig.lambda.tracingEnabled || Boolean(otel);
|
|
338
|
+
const layerArns = [...(envConfig.lambda.layers ?? []), ...(otelLayerArn ? [otelLayerArn] : [])];
|
|
305
339
|
if (layerArns.length > 5) {
|
|
306
|
-
throw new Error(`[venturekit] lambda.layers has ${layerArns.length} ARNs; Lambda allows at most 5.`);
|
|
340
|
+
throw new Error(`[venturekit] lambda.layers has ${layerArns.length} ARNs${otel ? ' (with the OpenTelemetry layer)' : ''}; Lambda allows at most 5.`);
|
|
307
341
|
}
|
|
308
|
-
this.lambdaLayers = layerArns.map((arn, i) => lambda.LayerVersion.fromLayerVersionArn(this, `lambda-layer-${i}`, arn));
|
|
342
|
+
this.lambdaLayers = layerArns.map((arn, i) => lambda.LayerVersion.fromLayerVersionArn(this, arn === otelLayerArn && i === layerArns.length - 1 ? 'lambda-layer-otel' : `lambda-layer-${i}`, arn));
|
|
309
343
|
this.skipBundling = props.skipBundling ?? false;
|
|
310
344
|
this.projectDir = props.projectDir;
|
|
311
345
|
// Stamp once per synth so every route Lambda in this deploy reports
|
|
@@ -344,7 +378,13 @@ export class VentureAppStack extends cdk.Stack {
|
|
|
344
378
|
// CFN's 500-resource limit on large projects is the reason the
|
|
345
379
|
// per-function layout was never adopted.
|
|
346
380
|
this.adminRoutePatterns = collectAdminRoutePatterns(infrastructure.auth ?? []);
|
|
347
|
-
|
|
381
|
+
this.legacyAdminGrants = usesLegacyAdminGrants(infrastructure.auth ?? []);
|
|
382
|
+
if (this.legacyAdminGrants) {
|
|
383
|
+
cdk.Annotations.of(this).addWarning(`[venturekit] auth[].adminRoutes is 'all': every Lambda in ${projectName}/${stage} shares one role ` +
|
|
384
|
+
`that can create users, set passwords and edit attributes on the user pool. List the routes that ` +
|
|
385
|
+
`call @venturekit/auth/server admin flows (e.g. adminRoutes: ['/auth/**']) to confine those grants.`);
|
|
386
|
+
}
|
|
387
|
+
else if (this.adminRoutePatterns.length > 0) {
|
|
348
388
|
this.authAdminRole = new iam.Role(this, 'AuthAdminRole', {
|
|
349
389
|
assumedBy: new iam.ServicePrincipal('lambda.amazonaws.com'),
|
|
350
390
|
description: `Execution role for ${projectName}-${stage} Lambdas allowed to call Cognito admin APIs (auth[].adminRoutes)`,
|
|
@@ -354,10 +394,33 @@ export class VentureAppStack extends cdk.Stack {
|
|
|
354
394
|
});
|
|
355
395
|
}
|
|
356
396
|
else if ((infrastructure.auth ?? []).length > 0) {
|
|
357
|
-
cdk.Annotations.of(this).
|
|
358
|
-
`
|
|
359
|
-
`
|
|
360
|
-
|
|
397
|
+
cdk.Annotations.of(this).addInfo(`[venturekit] auth intents declare no 'adminRoutes': no Lambda in ${projectName}/${stage} can call ` +
|
|
398
|
+
`Cognito admin APIs. Routes that run @venturekit/auth/server admin flows (sign-up, federated ` +
|
|
399
|
+
`sign-in, password reset, …) must be listed in auth[].adminRoutes.`);
|
|
400
|
+
}
|
|
401
|
+
// ─── System-login execution roles (databases[].logins) ──────
|
|
402
|
+
const primaryDb = imports.databaseEndpointsById?.values().next().value;
|
|
403
|
+
this.systemLoginPatterns = (primaryDb?.systemFunctions ?? []).map((p) => p.trim()).filter(Boolean);
|
|
404
|
+
if (primaryDb?.secretArn && primaryDb.systemSecretArn) {
|
|
405
|
+
this.dbLoginSecrets = { app: primaryDb.secretArn, system: primaryDb.systemSecretArn };
|
|
406
|
+
}
|
|
407
|
+
if (this.dbLoginSecrets && this.systemLoginPatterns.length > 0) {
|
|
408
|
+
const executionRole = (id, description) => new iam.Role(this, id, {
|
|
409
|
+
assumedBy: new iam.ServicePrincipal('lambda.amazonaws.com'),
|
|
410
|
+
description,
|
|
411
|
+
managedPolicies: [iam.ManagedPolicy.fromAwsManagedPolicyName('service-role/AWSLambdaBasicExecutionRole')],
|
|
412
|
+
});
|
|
413
|
+
this.dbSystemRole = executionRole('DbSystemRole', `Execution role for ${projectName}-${stage} Lambdas on the system database login (databases[].logins.system)`);
|
|
414
|
+
if (this.authAdminRole) {
|
|
415
|
+
this.authAdminDbSystemRole = executionRole('AuthAdminDbSystemRole', `Execution role for ${projectName}-${stage} Lambdas with Cognito admin grants on the system database login`);
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
if (otel?.applicationSignals) {
|
|
419
|
+
// CloudWatch Application Signals: the layer publishes service
|
|
420
|
+
// metrics and traces under this role.
|
|
421
|
+
for (const role of this.executionRoles) {
|
|
422
|
+
role.addManagedPolicy(iam.ManagedPolicy.fromAwsManagedPolicyName(APPLICATION_SIGNALS_POLICY));
|
|
423
|
+
}
|
|
361
424
|
}
|
|
362
425
|
if (this.vpc) {
|
|
363
426
|
// VPC-attached Lambdas need ENI-creation rights on cold start.
|
|
@@ -366,6 +429,9 @@ export class VentureAppStack extends cdk.Stack {
|
|
|
366
429
|
for (const role of this.executionRoles) {
|
|
367
430
|
role.addManagedPolicy(iam.ManagedPolicy.fromAwsManagedPolicyName('service-role/AWSLambdaVPCAccessExecutionRole'));
|
|
368
431
|
}
|
|
432
|
+
if (imports.internalClientsSecurityGroupId) {
|
|
433
|
+
this.internalClientsSecurityGroup = ec2.SecurityGroup.fromSecurityGroupId(this, 'InternalClientsSg', imports.internalClientsSecurityGroupId);
|
|
434
|
+
}
|
|
369
435
|
this.lambdaSecurityGroup = new ec2.SecurityGroup(this, 'LambdaSg', {
|
|
370
436
|
vpc: this.vpc,
|
|
371
437
|
description: `Shared SG for ${projectName}-${stage} Lambdas`,
|
|
@@ -476,7 +542,18 @@ export class VentureAppStack extends cdk.Stack {
|
|
|
476
542
|
// `--enable-source-maps` makes thrown errors point to the
|
|
477
543
|
// .ts source instead of bundled .js. Free win in CloudWatch.
|
|
478
544
|
NODE_OPTIONS: '--enable-source-maps',
|
|
545
|
+
// `lambda.otel`: the wrapper starts the layer's OpenTelemetry SDK
|
|
546
|
+
// (it appends its own `--require` to NODE_OPTIONS at startup).
|
|
547
|
+
...(otel
|
|
548
|
+
? otelEnvironment({
|
|
549
|
+
serviceName: otel.serviceName ?? `${projectName}-${stage}`,
|
|
550
|
+
applicationSignals: otel.applicationSignals === true,
|
|
551
|
+
})
|
|
552
|
+
: {}),
|
|
479
553
|
};
|
|
554
|
+
// Caches need the env map (endpoint + AUTH secret) and the Lambda SG.
|
|
555
|
+
this.wireCaches(infrastructure.caches ?? [], projectName, stage);
|
|
556
|
+
this.wireInternalRoutes(infrastructure.internal, projectName, stage);
|
|
480
557
|
// ─── Cross-tier env injection (cheap, static keys only) ────
|
|
481
558
|
// Per-intent keys go through helpers in sub-steps 9b/c/d. The
|
|
482
559
|
// ones below are stack-wide singletons — primary DB, primary
|
|
@@ -503,6 +580,11 @@ export class VentureAppStack extends cdk.Stack {
|
|
|
503
580
|
if (primaryIntent?.tenantRole) {
|
|
504
581
|
this.baseEnvVars['DB_TENANT_ROLE'] = primaryIntent.tenantRole;
|
|
505
582
|
}
|
|
583
|
+
// Two logins: every function is on the APP login unless its role is a
|
|
584
|
+
// system-login one (`loginEnvFor` switches the secret and DB_LOGIN).
|
|
585
|
+
if (primary.systemSecretArn) {
|
|
586
|
+
this.baseEnvVars['DB_LOGIN'] = 'app';
|
|
587
|
+
}
|
|
506
588
|
}
|
|
507
589
|
if (imports.storageBucketNamesById && imports.storageBucketNamesById.size > 0) {
|
|
508
590
|
const [, primaryBucket] = imports.storageBucketNamesById.entries().next().value;
|
|
@@ -567,6 +649,13 @@ export class VentureAppStack extends cdk.Stack {
|
|
|
567
649
|
this.baseEnvVars[`${prefix}_USER_POOL_ID`] = poolId;
|
|
568
650
|
if (clientId)
|
|
569
651
|
this.baseEnvVars[`${prefix}_APP_CLIENT_ID`] = clientId;
|
|
652
|
+
// The primary client's refresh-token rotation, so `refreshSession`
|
|
653
|
+
// takes the rotating flow (`@venturekit/auth` `loadAuthServerConfig`).
|
|
654
|
+
if (intent.clients?.[0]?.refreshTokenRotation?.enabled) {
|
|
655
|
+
this.baseEnvVars[`${prefix}_REFRESH_TOKEN_ROTATION`] = 'true';
|
|
656
|
+
if (intent === (infrastructure.auth ?? [])[0])
|
|
657
|
+
this.baseEnvVars['COGNITO_REFRESH_TOKEN_ROTATION'] = 'true';
|
|
658
|
+
}
|
|
570
659
|
}
|
|
571
660
|
this.baseEnvVars[`${prefix}_REGION`] = cdk.Stack.of(this).region;
|
|
572
661
|
}
|
|
@@ -723,14 +812,14 @@ export class VentureAppStack extends cdk.Stack {
|
|
|
723
812
|
this.baseEnvVars['COGNITO_CUSTOM_SENDER_KMS_KEY_ARN'] ??= keyArn;
|
|
724
813
|
}
|
|
725
814
|
// ─── Billing provider secret ARNs (billing intents) ─────
|
|
726
|
-
// `@venturekit/billing`'s
|
|
815
|
+
// `@venturekit/billing`'s resolveManzaConfig() reads the API key,
|
|
727
816
|
// account id, and webhook secret from the Secrets Manager entry named
|
|
728
|
-
// by `
|
|
817
|
+
// by `MANZA_SECRET_ARN`. Inject it here — the matching read grant is
|
|
729
818
|
// attached in applyImportedGrants — so a project that declares a
|
|
730
819
|
// billing intent needs no wiring of its own.
|
|
731
820
|
//
|
|
732
821
|
// The env var name is derived from the provider, not the intent id,
|
|
733
|
-
// because the runtime resolver is provider-scoped (one
|
|
822
|
+
// because the runtime resolver is provider-scoped (one Manza account
|
|
734
823
|
// per stage). The first declared intent for a given provider wins,
|
|
735
824
|
// mirroring COGNITO_FEDERATED_<PROVIDER>_SECRET_ARN.
|
|
736
825
|
if (imports.billingSecretArnsById) {
|
|
@@ -738,7 +827,9 @@ export class VentureAppStack extends cdk.Stack {
|
|
|
738
827
|
const intent = infrastructure.billing?.find((b) => b.id === id);
|
|
739
828
|
if (!intent)
|
|
740
829
|
continue;
|
|
741
|
-
|
|
830
|
+
// `'zazu'` is the deprecated name of `'manza'`.
|
|
831
|
+
const provider = intent.provider === 'zazu' ? 'manza' : intent.provider;
|
|
832
|
+
const key = `${provider.toUpperCase()}_SECRET_ARN`;
|
|
742
833
|
if (!(key in this.baseEnvVars)) {
|
|
743
834
|
this.baseEnvVars[key] = arn;
|
|
744
835
|
}
|
|
@@ -785,7 +876,11 @@ export class VentureAppStack extends cdk.Stack {
|
|
|
785
876
|
stage,
|
|
786
877
|
envConfig,
|
|
787
878
|
lambdaRole: this.lambdaRole,
|
|
879
|
+
roleFor: (routeKey) => this.roleFor({ wsRoute: routeKey }),
|
|
880
|
+
envFor: (routeKey) => this.loginEnvFor(this.roleFor({ wsRoute: routeKey })),
|
|
881
|
+
extraRoles: this.executionRoles.filter((r) => r !== this.lambdaRole),
|
|
788
882
|
lambdaSecurityGroup: this.lambdaSecurityGroup,
|
|
883
|
+
extraSecurityGroups: this.internalClientsSecurityGroup ? [this.internalClientsSecurityGroup] : [],
|
|
789
884
|
vpc: this.vpc,
|
|
790
885
|
baseEnvVars: this.baseEnvVars,
|
|
791
886
|
skipBundling: this.skipBundling,
|
|
@@ -1089,11 +1184,10 @@ export class VentureAppStack extends cdk.Stack {
|
|
|
1089
1184
|
environment: {
|
|
1090
1185
|
...this.baseEnvVars,
|
|
1091
1186
|
...lambdaConfig.environmentVariables,
|
|
1187
|
+
...this.loginEnvFor(this.roleForRoute(route.apiPath)),
|
|
1092
1188
|
},
|
|
1093
1189
|
logGroup,
|
|
1094
|
-
tracing:
|
|
1095
|
-
? lambda.Tracing.ACTIVE
|
|
1096
|
-
: lambda.Tracing.DISABLED,
|
|
1190
|
+
tracing: this.tracingActive ? lambda.Tracing.ACTIVE : lambda.Tracing.DISABLED,
|
|
1097
1191
|
reservedConcurrentExecutions: lambdaConfig.reservedConcurrency,
|
|
1098
1192
|
role: this.roleForRoute(route.apiPath),
|
|
1099
1193
|
// Attach to the VPC iff one exists. Route Lambdas without a VPC
|
|
@@ -1104,7 +1198,7 @@ export class VentureAppStack extends cdk.Stack {
|
|
|
1104
1198
|
? {
|
|
1105
1199
|
vpc: this.vpc,
|
|
1106
1200
|
vpcSubnets: { subnetType: ec2.SubnetType.PRIVATE_WITH_EGRESS },
|
|
1107
|
-
securityGroups:
|
|
1201
|
+
securityGroups: this.lambdaSecurityGroups,
|
|
1108
1202
|
}
|
|
1109
1203
|
: {}),
|
|
1110
1204
|
});
|
|
@@ -1115,6 +1209,10 @@ export class VentureAppStack extends cdk.Stack {
|
|
|
1115
1209
|
cdk.Tags.of(fn).add('deployedAt', this.deployedAt);
|
|
1116
1210
|
cdk.Tags.of(logGroup).add('deployedAt', this.deployedAt);
|
|
1117
1211
|
this.routeLambdas.set(route.slug, fn);
|
|
1212
|
+
// An internal route is on no public API: other ventures invoke its
|
|
1213
|
+
// function directly (`internalFetch`), so a public request is a 404.
|
|
1214
|
+
if (isInternalRoute(route.apiPath))
|
|
1215
|
+
return;
|
|
1118
1216
|
// Add the HttpRoute on the imported API. `HttpRouteKey.with`
|
|
1119
1217
|
// builds the canonical `${METHOD} ${path}` string AWS uses
|
|
1120
1218
|
// internally; passing path + method separately keeps us honest
|
|
@@ -1125,40 +1223,110 @@ export class VentureAppStack extends cdk.Stack {
|
|
|
1125
1223
|
integration: new apigatewayv2Integrations.HttpLambdaIntegration(`${resourceId}-integration`, fn),
|
|
1126
1224
|
});
|
|
1127
1225
|
}
|
|
1226
|
+
/** Every Lambda's groups: its own, and the VPC's internal clients group. */
|
|
1227
|
+
get lambdaSecurityGroups() {
|
|
1228
|
+
return [this.lambdaSecurityGroup, ...(this.internalClientsSecurityGroup ? [this.internalClientsSecurityGroup] : [])];
|
|
1229
|
+
}
|
|
1128
1230
|
/**
|
|
1129
1231
|
* Every execution role in this stack: the shared role, plus the
|
|
1130
1232
|
* auth-admin role when one exists. Baseline grants (secrets, S3,
|
|
1131
1233
|
* SES, rate-limit table, …) are applied to all of them.
|
|
1132
1234
|
*/
|
|
1133
1235
|
get executionRoles() {
|
|
1134
|
-
return
|
|
1236
|
+
return [this.lambdaRole, this.authAdminRole, this.dbSystemRole, this.authAdminDbSystemRole].filter((r) => r !== undefined);
|
|
1237
|
+
}
|
|
1238
|
+
/** The roles whose functions connect as the system database login. */
|
|
1239
|
+
get systemLoginRoles() {
|
|
1240
|
+
return [this.dbSystemRole, this.authAdminDbSystemRole].filter((r) => r !== undefined);
|
|
1135
1241
|
}
|
|
1136
1242
|
/**
|
|
1137
|
-
*
|
|
1138
|
-
*
|
|
1139
|
-
|
|
1243
|
+
* The role for a function: Cognito admin or not (`auth[].adminRoutes`),
|
|
1244
|
+
* system database login or not (`databases[].logins.system.functions`).
|
|
1245
|
+
*/
|
|
1246
|
+
roleFor(subject) {
|
|
1247
|
+
const admin = Boolean(this.authAdminRole) && matchesAdminRoute(this.adminRoutePatterns, subject);
|
|
1248
|
+
const system = Boolean(this.dbSystemRole) && matchesFunctionPattern(this.systemLoginPatterns, subject);
|
|
1249
|
+
if (admin && system)
|
|
1250
|
+
return this.authAdminDbSystemRole;
|
|
1251
|
+
if (admin)
|
|
1252
|
+
return this.authAdminRole;
|
|
1253
|
+
if (system)
|
|
1254
|
+
return this.dbSystemRole;
|
|
1255
|
+
return this.lambdaRole;
|
|
1256
|
+
}
|
|
1257
|
+
/** The database env a function running as `role` gets on top of the base env. */
|
|
1258
|
+
loginEnvFor(role) {
|
|
1259
|
+
if (!this.dbLoginSecrets || !role || !this.systemLoginRoles.includes(role))
|
|
1260
|
+
return {};
|
|
1261
|
+
return { DB_SECRET_ARN: this.dbLoginSecrets.system, DB_LOGIN: 'system' };
|
|
1262
|
+
}
|
|
1263
|
+
/**
|
|
1264
|
+
* Roles that receive the Cognito admin grants: the shared role under
|
|
1265
|
+
* the explicit legacy layout (`adminRoutes: 'all'`), the auth-admin role
|
|
1266
|
+
* when routes are listed, and none otherwise.
|
|
1140
1267
|
*/
|
|
1141
1268
|
get cognitoAdminRoles() {
|
|
1142
|
-
|
|
1269
|
+
if (this.legacyAdminGrants)
|
|
1270
|
+
return [this.lambdaRole, ...(this.dbSystemRole ? [this.dbSystemRole] : [])];
|
|
1271
|
+
return [this.authAdminRole, this.authAdminDbSystemRole].filter((r) => r !== undefined);
|
|
1143
1272
|
}
|
|
1144
1273
|
/** Apply one grant to every execution role. */
|
|
1145
1274
|
grantEachRole(grant) {
|
|
1146
1275
|
for (const role of this.executionRoles)
|
|
1147
1276
|
grant(role);
|
|
1148
1277
|
}
|
|
1278
|
+
/**
|
|
1279
|
+
* Caches: the data stack's cache security group admits THIS stack's
|
|
1280
|
+
* Lambda security group only, and every Lambda gets the endpoint and
|
|
1281
|
+
* its AUTH token's secret (`VENTURE_CACHE_<ID>_HOST` / `_PORT` /
|
|
1282
|
+
* `_AUTH_SECRET_ARN`; `cacheConnection(id)` in `@venturekit/runtime`
|
|
1283
|
+
* turns them into TLS + AUTH connection options).
|
|
1284
|
+
*/
|
|
1285
|
+
wireCaches(caches, projectName, stage) {
|
|
1286
|
+
if (!this.lambdaSecurityGroup)
|
|
1287
|
+
return;
|
|
1288
|
+
for (const cache of caches) {
|
|
1289
|
+
const cacheSg = ec2.SecurityGroup.fromSecurityGroupId(this, `imported-cache-sg-${cache.id}`, readSsmString(this, dataKeys.cacheSecurityGroupId(projectName, stage, cache.id)), { mutable: true });
|
|
1290
|
+
cacheSg.connections.allowFrom(this.lambdaSecurityGroup, ec2.Port.tcp(6379), `Allow ${projectName}-${stage} Lambdas to connect to cache ${cache.id}`);
|
|
1291
|
+
const prefix = `VENTURE_CACHE_${upperSnake(cache.id)}`;
|
|
1292
|
+
const secretArn = readSsmString(this, dataKeys.cacheAuthSecretArn(projectName, stage, cache.id));
|
|
1293
|
+
this.baseEnvVars[`${prefix}_HOST`] = readSsmString(this, dataKeys.cacheEndpoint(projectName, stage, cache.id));
|
|
1294
|
+
this.baseEnvVars[`${prefix}_PORT`] = readSsmString(this, dataKeys.cachePort(projectName, stage, cache.id));
|
|
1295
|
+
this.baseEnvVars[`${prefix}_AUTH_SECRET_ARN`] = secretArn;
|
|
1296
|
+
const secret = secretsmanager.Secret.fromSecretCompleteArn(this, `imported-cache-auth-${cache.id}`, secretArn);
|
|
1297
|
+
this.grantEachRole((role) => secret.grantRead(role));
|
|
1298
|
+
}
|
|
1299
|
+
}
|
|
1300
|
+
/**
|
|
1301
|
+
* Internal routes (`internal` intent): the pair secrets this venture keeps
|
|
1302
|
+
* for its callers, and the rights to call the ventures it calls.
|
|
1303
|
+
*/
|
|
1304
|
+
wireInternalRoutes(internal, projectName, stage) {
|
|
1305
|
+
const secrets = createInternalCallerSecrets(this, projectName, stage, internal, this.isStrictRetain);
|
|
1306
|
+
if (secrets.size > 0) {
|
|
1307
|
+
for (const [caller, secret] of secrets) {
|
|
1308
|
+
this.grantEachRole((role) => secret.grantRead(role));
|
|
1309
|
+
// What the caller's `vk deploy` checks before deploying (`verifyInternalCalls`).
|
|
1310
|
+
writeSsm(this, internalKeys.callerSecretArn(projectName, stage, caller), secret.secretArn, {
|
|
1311
|
+
description: `Pair secret ${caller} → ${projectName} internal routes`,
|
|
1312
|
+
});
|
|
1313
|
+
}
|
|
1314
|
+
this.baseEnvVars.VENTURE_INTERNAL_CALLER_SECRET_ARNS = JSON.stringify(Object.fromEntries([...secrets].map(([caller, secret]) => [caller, secret.secretArn])));
|
|
1315
|
+
// Who may call — what a caller's `vk deploy` checks it is part of.
|
|
1316
|
+
writeSsm(this, internalKeys.callers(projectName, stage), [...secrets.keys()].join(','), {
|
|
1317
|
+
description: `Ventures allowed to call ${projectName} internal routes`,
|
|
1318
|
+
});
|
|
1319
|
+
}
|
|
1320
|
+
const statements = internalCallStatements(this, projectName, stage, internal);
|
|
1321
|
+
this.grantEachRole((role) => statements.forEach((s) => role.addToPolicy(s)));
|
|
1322
|
+
}
|
|
1149
1323
|
/** The role a route Lambda runs as — auth-admin when its API path matches `adminRoutes`. */
|
|
1150
1324
|
roleForRoute(apiPath) {
|
|
1151
|
-
|
|
1152
|
-
return this.authAdminRole;
|
|
1153
|
-
}
|
|
1154
|
-
return this.lambdaRole;
|
|
1325
|
+
return this.roleFor({ apiPath });
|
|
1155
1326
|
}
|
|
1156
|
-
/** The role a standalone / queue / schedule Lambda runs as
|
|
1327
|
+
/** The role a standalone / queue / schedule Lambda runs as (`fn:<id>`). */
|
|
1157
1328
|
roleForFunction(id) {
|
|
1158
|
-
|
|
1159
|
-
return this.authAdminRole;
|
|
1160
|
-
}
|
|
1161
|
-
return this.lambdaRole;
|
|
1329
|
+
return this.roleFor({ functionId: id });
|
|
1162
1330
|
}
|
|
1163
1331
|
/**
|
|
1164
1332
|
* Build a Lambda function with the standard app-stack properties
|
|
@@ -1190,14 +1358,8 @@ export class VentureAppStack extends cdk.Stack {
|
|
|
1190
1358
|
memorySize: lambdaConfig.memoryMb,
|
|
1191
1359
|
timeout: cdk.Duration.seconds(lambdaConfig.timeoutSec),
|
|
1192
1360
|
description,
|
|
1193
|
-
environment: {
|
|
1194
|
-
...this.baseEnvVars,
|
|
1195
|
-
...lambdaConfig.environmentVariables,
|
|
1196
|
-
},
|
|
1197
1361
|
logGroup,
|
|
1198
|
-
tracing:
|
|
1199
|
-
? lambda.Tracing.ACTIVE
|
|
1200
|
-
: lambda.Tracing.DISABLED,
|
|
1362
|
+
tracing: this.tracingActive ? lambda.Tracing.ACTIVE : lambda.Tracing.DISABLED,
|
|
1201
1363
|
reservedConcurrentExecutions: lambdaConfig.reservedConcurrency,
|
|
1202
1364
|
role: this.lambdaRole,
|
|
1203
1365
|
...(this.vpc && this.lambdaSecurityGroup
|
|
@@ -1208,6 +1370,11 @@ export class VentureAppStack extends cdk.Stack {
|
|
|
1208
1370
|
}
|
|
1209
1371
|
: {}),
|
|
1210
1372
|
...overrides,
|
|
1373
|
+
// The database login follows the role (a system-login role reads the system secret).
|
|
1374
|
+
environment: {
|
|
1375
|
+
...(overrides.environment ?? { ...this.baseEnvVars, ...lambdaConfig.environmentVariables }),
|
|
1376
|
+
...this.loginEnvFor(overrides.role),
|
|
1377
|
+
},
|
|
1211
1378
|
});
|
|
1212
1379
|
}
|
|
1213
1380
|
/**
|
|
@@ -1610,7 +1777,14 @@ export class VentureAppStack extends cdk.Stack {
|
|
|
1610
1777
|
if (!db.secretArn)
|
|
1611
1778
|
continue;
|
|
1612
1779
|
const importedSecret = secretsmanager.Secret.fromSecretCompleteArn(this, `imported-db-secret-${id}`, db.secretArn);
|
|
1780
|
+
// The app login's secret to every role; the system login's to the
|
|
1781
|
+
// system-login roles ONLY.
|
|
1613
1782
|
this.grantEachRole((role) => importedSecret.grantRead(role));
|
|
1783
|
+
if (db.systemSecretArn && this.systemLoginRoles.length > 0) {
|
|
1784
|
+
const systemSecret = secretsmanager.Secret.fromSecretCompleteArn(this, `imported-db-system-secret-${id}`, db.systemSecretArn);
|
|
1785
|
+
for (const role of this.systemLoginRoles)
|
|
1786
|
+
systemSecret.grantRead(role);
|
|
1787
|
+
}
|
|
1614
1788
|
}
|
|
1615
1789
|
}
|
|
1616
1790
|
// ── Federated provider secrets ────────────────────────────
|
|
@@ -1679,8 +1853,9 @@ export class VentureAppStack extends cdk.Stack {
|
|
|
1679
1853
|
}
|
|
1680
1854
|
}
|
|
1681
1855
|
// ── Cognito admin actions per user pool ──────────────────
|
|
1682
|
-
// Attached to the auth-admin role
|
|
1683
|
-
//
|
|
1856
|
+
// Attached to the auth-admin role when `adminRoutes` lists routes,
|
|
1857
|
+
// to the shared role under `adminRoutes: 'all'`, and to no role
|
|
1858
|
+
// otherwise (see `cognitoAdminRoles`).
|
|
1684
1859
|
if (imports.userPoolArnsByAuthId) {
|
|
1685
1860
|
for (const [, poolArn] of imports.userPoolArnsByAuthId) {
|
|
1686
1861
|
// Scope to the Cognito admin actions `@venturekit/auth/server`
|