@jaypie/constructs 1.1.52 → 1.1.54
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/cjs/JaypieAccountLoggingBucket.d.ts +60 -0
- package/dist/cjs/JaypieDatadogBucket.d.ts +45 -0
- package/dist/cjs/JaypieDatadogForwarder.d.ts +76 -0
- package/dist/cjs/JaypieEventsRule.d.ts +45 -0
- package/dist/cjs/JaypieOrganizationTrail.d.ts +62 -0
- package/dist/cjs/constants.d.ts +151 -0
- package/dist/cjs/helpers/extendDatadogRole.d.ts +31 -0
- package/dist/cjs/helpers/index.d.ts +4 -0
- package/dist/cjs/helpers/isValidHostname.d.ts +1 -0
- package/dist/cjs/helpers/isValidSubdomain.d.ts +1 -0
- package/dist/cjs/helpers/mergeDomain.d.ts +1 -0
- package/dist/cjs/index.cjs +864 -157
- package/dist/cjs/index.cjs.map +1 -1
- package/dist/cjs/index.d.ts +6 -1
- package/dist/esm/JaypieAccountLoggingBucket.d.ts +60 -0
- package/dist/esm/JaypieDatadogBucket.d.ts +45 -0
- package/dist/esm/JaypieDatadogForwarder.d.ts +76 -0
- package/dist/esm/JaypieEventsRule.d.ts +45 -0
- package/dist/esm/JaypieOrganizationTrail.d.ts +62 -0
- package/dist/esm/constants.d.ts +151 -0
- package/dist/esm/helpers/extendDatadogRole.d.ts +31 -0
- package/dist/esm/helpers/index.d.ts +4 -0
- package/dist/esm/helpers/isValidHostname.d.ts +1 -0
- package/dist/esm/helpers/isValidSubdomain.d.ts +1 -0
- package/dist/esm/helpers/mergeDomain.d.ts +1 -0
- package/dist/esm/index.d.ts +6 -1
- package/dist/esm/index.js +711 -9
- package/dist/esm/index.js.map +1 -1
- package/package.json +2 -3
package/dist/cjs/index.cjs
CHANGED
|
@@ -1,25 +1,27 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var cdk = require('
|
|
3
|
+
var cdk = require('aws-cdk-lib');
|
|
4
|
+
var s3 = require('aws-cdk-lib/aws-s3');
|
|
4
5
|
var constructs = require('constructs');
|
|
5
|
-
var cdk$1 = require('aws-cdk-lib');
|
|
6
6
|
var acm = require('aws-cdk-lib/aws-certificatemanager');
|
|
7
7
|
var apiGateway = require('aws-cdk-lib/aws-apigateway');
|
|
8
8
|
var route53 = require('aws-cdk-lib/aws-route53');
|
|
9
9
|
var route53Targets = require('aws-cdk-lib/aws-route53-targets');
|
|
10
10
|
var secretsmanager = require('aws-cdk-lib/aws-secretsmanager');
|
|
11
11
|
var datadogCdkConstructsV2 = require('datadog-cdk-constructs-v2');
|
|
12
|
+
var errors = require('@jaypie/errors');
|
|
13
|
+
var awsIam = require('aws-cdk-lib/aws-iam');
|
|
12
14
|
var lambda = require('aws-cdk-lib/aws-lambda');
|
|
13
15
|
var logDestinations = require('aws-cdk-lib/aws-logs-destinations');
|
|
14
|
-
var s3 = require('aws-cdk-lib/aws-s3');
|
|
15
16
|
var s3n = require('aws-cdk-lib/aws-s3-notifications');
|
|
16
17
|
var sqs = require('aws-cdk-lib/aws-sqs');
|
|
17
18
|
var lambdaEventSources = require('aws-cdk-lib/aws-lambda-event-sources');
|
|
18
|
-
var
|
|
19
|
+
var awsEvents = require('aws-cdk-lib/aws-events');
|
|
20
|
+
var awsEventsTargets = require('aws-cdk-lib/aws-events-targets');
|
|
19
21
|
var awsLogs = require('aws-cdk-lib/aws-logs');
|
|
22
|
+
var awsCloudtrail = require('aws-cdk-lib/aws-cloudtrail');
|
|
20
23
|
var awsSso = require('aws-cdk-lib/aws-sso');
|
|
21
24
|
var awsSam = require('aws-cdk-lib/aws-sam');
|
|
22
|
-
var errors = require('@jaypie/errors');
|
|
23
25
|
var cloudfront = require('aws-cdk-lib/aws-cloudfront');
|
|
24
26
|
var origins = require('aws-cdk-lib/aws-cloudfront-origins');
|
|
25
27
|
|
|
@@ -40,7 +42,8 @@ function _interopNamespaceDefault(e) {
|
|
|
40
42
|
return Object.freeze(n);
|
|
41
43
|
}
|
|
42
44
|
|
|
43
|
-
var cdk__namespace = /*#__PURE__*/_interopNamespaceDefault(cdk
|
|
45
|
+
var cdk__namespace = /*#__PURE__*/_interopNamespaceDefault(cdk);
|
|
46
|
+
var s3__namespace = /*#__PURE__*/_interopNamespaceDefault(s3);
|
|
44
47
|
var acm__namespace = /*#__PURE__*/_interopNamespaceDefault(acm);
|
|
45
48
|
var apiGateway__namespace = /*#__PURE__*/_interopNamespaceDefault(apiGateway);
|
|
46
49
|
var route53__namespace = /*#__PURE__*/_interopNamespaceDefault(route53);
|
|
@@ -48,13 +51,231 @@ var route53Targets__namespace = /*#__PURE__*/_interopNamespaceDefault(route53Tar
|
|
|
48
51
|
var secretsmanager__namespace = /*#__PURE__*/_interopNamespaceDefault(secretsmanager);
|
|
49
52
|
var lambda__namespace = /*#__PURE__*/_interopNamespaceDefault(lambda);
|
|
50
53
|
var logDestinations__namespace = /*#__PURE__*/_interopNamespaceDefault(logDestinations);
|
|
51
|
-
var s3__namespace = /*#__PURE__*/_interopNamespaceDefault(s3);
|
|
52
54
|
var s3n__namespace = /*#__PURE__*/_interopNamespaceDefault(s3n);
|
|
53
55
|
var sqs__namespace = /*#__PURE__*/_interopNamespaceDefault(sqs);
|
|
54
56
|
var lambdaEventSources__namespace = /*#__PURE__*/_interopNamespaceDefault(lambdaEventSources);
|
|
55
57
|
var cloudfront__namespace = /*#__PURE__*/_interopNamespaceDefault(cloudfront);
|
|
56
58
|
var origins__namespace = /*#__PURE__*/_interopNamespaceDefault(origins);
|
|
57
59
|
|
|
60
|
+
const CDK$2 = {
|
|
61
|
+
ACCOUNT: {
|
|
62
|
+
DEVELOPMENT: "development",
|
|
63
|
+
MANAGEMENT: "management",
|
|
64
|
+
OPERATIONS: "operations",
|
|
65
|
+
PRODUCTION: "production",
|
|
66
|
+
SANDBOX: "sandbox",
|
|
67
|
+
SECURITY: "security",
|
|
68
|
+
STAGE: "stage",
|
|
69
|
+
},
|
|
70
|
+
BUILD: {
|
|
71
|
+
CONFIG: {
|
|
72
|
+
ALL: "all",
|
|
73
|
+
API: "api",
|
|
74
|
+
INFRASTRUCTURE: "infrastructure",
|
|
75
|
+
NONE: "none",
|
|
76
|
+
WEB: "web",
|
|
77
|
+
},
|
|
78
|
+
PERSONAL: "personal",
|
|
79
|
+
/**
|
|
80
|
+
* @deprecated rename "ephemeral" to "personal" (since 2/24/2025)
|
|
81
|
+
*/
|
|
82
|
+
EPHEMERAL: "ephemeral",
|
|
83
|
+
/**
|
|
84
|
+
* @deprecated as even "ephemeral" builds have static assets (since 7/6/2024)
|
|
85
|
+
*/
|
|
86
|
+
STATIC: "static",
|
|
87
|
+
},
|
|
88
|
+
CREATION: {
|
|
89
|
+
CDK: "cdk",
|
|
90
|
+
CLOUDFORMATION_TEMPLATE: "template",
|
|
91
|
+
MANUAL: "manual",
|
|
92
|
+
},
|
|
93
|
+
DATADOG: {
|
|
94
|
+
SITE: "datadoghq.com",
|
|
95
|
+
LAYER: {
|
|
96
|
+
// https://docs.datadoghq.com/serverless/aws_lambda/installation/nodejs/?tab=awscdk
|
|
97
|
+
NODE: 127, // 127 on 9/12/2025
|
|
98
|
+
EXTENSION: 86, // 86 on 9/12/2025
|
|
99
|
+
},
|
|
100
|
+
},
|
|
101
|
+
DEFAULT: {
|
|
102
|
+
REGION: "us-east-1",
|
|
103
|
+
},
|
|
104
|
+
DNS: {
|
|
105
|
+
CONFIG: {
|
|
106
|
+
TTL: 300, // 5 minutes in seconds for Route53
|
|
107
|
+
},
|
|
108
|
+
RECORD: {
|
|
109
|
+
A: "A",
|
|
110
|
+
CNAME: "CNAME",
|
|
111
|
+
MX: "MX",
|
|
112
|
+
NS: "NS",
|
|
113
|
+
TXT: "TXT",
|
|
114
|
+
},
|
|
115
|
+
},
|
|
116
|
+
DURATION: {
|
|
117
|
+
EXPRESS_API: 30,
|
|
118
|
+
LAMBDA_MAXIMUM: 900,
|
|
119
|
+
LAMBDA_WORKER: 900,
|
|
120
|
+
},
|
|
121
|
+
ENV: {
|
|
122
|
+
DEMO: "demo", // Mirror of production
|
|
123
|
+
DEVELOPMENT: "development", // Internal most stable development space
|
|
124
|
+
/** @deprecated */ EPHEMERAL: "ephemeral", // Alias for "build"
|
|
125
|
+
LOCAL: "local",
|
|
126
|
+
/** @deprecated */ MAIN: "main", // Alias for development
|
|
127
|
+
META: "meta", // For non-environment/infrastructure stacks
|
|
128
|
+
PERSONAL: "personal", // Personal builds using resources provided by sandbox
|
|
129
|
+
PREVIEW: "preview", // External next thing to be released
|
|
130
|
+
PRODUCTION: "production",
|
|
131
|
+
RELEASE: "release", // Internal next thing to be released
|
|
132
|
+
REVIEW: "review", // Internal place to collaborate on issues
|
|
133
|
+
SANDBOX: "sandbox", // Internal build space with no guaranteed longevity
|
|
134
|
+
TRAINING: "training", // aka "test"; mirror of production for external audiences
|
|
135
|
+
},
|
|
136
|
+
HOST: {
|
|
137
|
+
APEX: "@",
|
|
138
|
+
},
|
|
139
|
+
IMPORT: {
|
|
140
|
+
DATADOG_LOG_FORWARDER: "account-datadog-forwarder",
|
|
141
|
+
DATADOG_ROLE: "account-datadog-role",
|
|
142
|
+
DATADOG_SECRET: "account-datadog-secret",
|
|
143
|
+
LOG_BUCKET: "account-log-bucket",
|
|
144
|
+
OIDC_PROVIDER: "github-oidc-provider",
|
|
145
|
+
},
|
|
146
|
+
LAMBDA: {
|
|
147
|
+
LOG_RETENTION: 90,
|
|
148
|
+
MEMORY_SIZE: 1024,
|
|
149
|
+
},
|
|
150
|
+
PRINCIPAL: {
|
|
151
|
+
ROUTE53: "route53.amazonaws.com",
|
|
152
|
+
},
|
|
153
|
+
PRINCIPAL_TYPE: {
|
|
154
|
+
GROUP: "GROUP",
|
|
155
|
+
USER: "USER",
|
|
156
|
+
},
|
|
157
|
+
PROJECT: {
|
|
158
|
+
INFRASTRUCTURE: "infrastructure",
|
|
159
|
+
},
|
|
160
|
+
ROLE: {
|
|
161
|
+
API: "api",
|
|
162
|
+
DEPLOY: "deploy",
|
|
163
|
+
HOSTING: "hosting",
|
|
164
|
+
MONITORING: "monitoring",
|
|
165
|
+
NETWORKING: "networking",
|
|
166
|
+
PROCESSING: "processing",
|
|
167
|
+
SECURITY: "security",
|
|
168
|
+
STACK: "stack",
|
|
169
|
+
STORAGE: "storage",
|
|
170
|
+
TOY: "toy",
|
|
171
|
+
},
|
|
172
|
+
SERVICE: {
|
|
173
|
+
DATADOG: "datadog",
|
|
174
|
+
INFRASTRUCTURE: "infrastructure",
|
|
175
|
+
LIBRARIES: "libraries",
|
|
176
|
+
NONE: "none",
|
|
177
|
+
SSO: "sso",
|
|
178
|
+
TRACE: "trace",
|
|
179
|
+
},
|
|
180
|
+
TAG: {
|
|
181
|
+
BUILD_DATE: "buildDate",
|
|
182
|
+
BUILD_HEX: "buildHex",
|
|
183
|
+
BUILD_NUMBER: "buildNumber",
|
|
184
|
+
BUILD_TIME: "buildTime",
|
|
185
|
+
BUILD_TYPE: "buildType",
|
|
186
|
+
COMMIT: "commit",
|
|
187
|
+
CREATION: "creation",
|
|
188
|
+
ENV: "env",
|
|
189
|
+
NONCE: "nonce",
|
|
190
|
+
PROJECT: "project",
|
|
191
|
+
ROLE: "role",
|
|
192
|
+
SERVICE: "service",
|
|
193
|
+
SPONSOR: "sponsor",
|
|
194
|
+
STACK: "stack",
|
|
195
|
+
STACK_SHA: "stackSha",
|
|
196
|
+
VENDOR: "vendor",
|
|
197
|
+
VERSION: "version",
|
|
198
|
+
},
|
|
199
|
+
TARGET_TYPE: {
|
|
200
|
+
AWS_ACCOUNT: "AWS_ACCOUNT",
|
|
201
|
+
},
|
|
202
|
+
VENDOR: {
|
|
203
|
+
ANTHROPIC: "anthropic",
|
|
204
|
+
AUTH0: "auth0",
|
|
205
|
+
DATADOG: "datadog",
|
|
206
|
+
KNOWTRACE: "knowtrace",
|
|
207
|
+
MONGODB: "mongodb",
|
|
208
|
+
OPENAI: "openai",
|
|
209
|
+
SPLINTERLANDS: "splinterlands",
|
|
210
|
+
},
|
|
211
|
+
};
|
|
212
|
+
|
|
213
|
+
class JaypieAccountLoggingBucket extends constructs.Construct {
|
|
214
|
+
/**
|
|
215
|
+
* Create a new account-wide logging S3 bucket with lifecycle policies and export
|
|
216
|
+
*/
|
|
217
|
+
constructor(scope, idOrProps, propsOrUndefined) {
|
|
218
|
+
// Handle overloaded constructor signatures
|
|
219
|
+
let props;
|
|
220
|
+
let id;
|
|
221
|
+
if (typeof idOrProps === "string") {
|
|
222
|
+
// First param is ID, second is props
|
|
223
|
+
props = propsOrUndefined || {};
|
|
224
|
+
id = idOrProps;
|
|
225
|
+
}
|
|
226
|
+
else {
|
|
227
|
+
// First param is props
|
|
228
|
+
props = idOrProps || {};
|
|
229
|
+
id = props.id || "AccountLoggingBucket";
|
|
230
|
+
}
|
|
231
|
+
super(scope, id);
|
|
232
|
+
// Generate default bucket name with PROJECT_NONCE
|
|
233
|
+
const defaultBucketName = process.env.PROJECT_NONCE
|
|
234
|
+
? `account-logging-stack-${process.env.PROJECT_NONCE.toLowerCase()}`
|
|
235
|
+
: "account-logging-stack";
|
|
236
|
+
// Extract Jaypie-specific options
|
|
237
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
238
|
+
const { bucketName = defaultBucketName, createOutput = true, expirationDays = 365, exportName = CDK$2.IMPORT.LOG_BUCKET, glacierTransitionDays = 180, id: _id, infrequentAccessTransitionDays = 30, outputDescription = "Account-wide logging bucket", project, service = CDK$2.SERVICE.INFRASTRUCTURE, ...bucketProps } = props;
|
|
239
|
+
// Create the bucket with lifecycle rules
|
|
240
|
+
this.bucket = new s3.Bucket(this, "Bucket", {
|
|
241
|
+
accessControl: s3.BucketAccessControl.LOG_DELIVERY_WRITE,
|
|
242
|
+
bucketName,
|
|
243
|
+
lifecycleRules: [
|
|
244
|
+
{
|
|
245
|
+
expiration: cdk__namespace.Duration.days(expirationDays),
|
|
246
|
+
transitions: [
|
|
247
|
+
{
|
|
248
|
+
storageClass: s3.StorageClass.INFREQUENT_ACCESS,
|
|
249
|
+
transitionAfter: cdk__namespace.Duration.days(infrequentAccessTransitionDays),
|
|
250
|
+
},
|
|
251
|
+
{
|
|
252
|
+
storageClass: s3.StorageClass.GLACIER,
|
|
253
|
+
transitionAfter: cdk__namespace.Duration.days(glacierTransitionDays),
|
|
254
|
+
},
|
|
255
|
+
],
|
|
256
|
+
},
|
|
257
|
+
],
|
|
258
|
+
...bucketProps,
|
|
259
|
+
});
|
|
260
|
+
// Add tags
|
|
261
|
+
cdk__namespace.Tags.of(this.bucket).add(CDK$2.TAG.ROLE, CDK$2.ROLE.MONITORING);
|
|
262
|
+
if (service) {
|
|
263
|
+
cdk__namespace.Tags.of(this.bucket).add(CDK$2.TAG.SERVICE, service);
|
|
264
|
+
}
|
|
265
|
+
if (project) {
|
|
266
|
+
cdk__namespace.Tags.of(this.bucket).add(CDK$2.TAG.PROJECT, project);
|
|
267
|
+
}
|
|
268
|
+
// Create CloudFormation output if enabled
|
|
269
|
+
if (createOutput) {
|
|
270
|
+
new cdk__namespace.CfnOutput(this, "BucketNameOutput", {
|
|
271
|
+
description: outputDescription,
|
|
272
|
+
exportName,
|
|
273
|
+
value: this.bucket.bucketName,
|
|
274
|
+
});
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
|
|
58
279
|
function addDatadogLayers(lambdaFunction, options = {}) {
|
|
59
280
|
const datadogApiKeyArn = options?.datadogApiKeyArn;
|
|
60
281
|
const resolvedDatadogApiKeyArn = datadogApiKeyArn ||
|
|
@@ -71,8 +292,8 @@ function addDatadogLayers(lambdaFunction, options = {}) {
|
|
|
71
292
|
DD_PROFILING_ENABLED: "false",
|
|
72
293
|
DD_SERVERLESS_APPSEC_ENABLED: "false",
|
|
73
294
|
DD_SERVICE: process.env.PROJECT_SERVICE || "",
|
|
74
|
-
DD_SITE:
|
|
75
|
-
DD_TAGS: `${
|
|
295
|
+
DD_SITE: CDK$2.DATADOG.SITE,
|
|
296
|
+
DD_TAGS: `${CDK$2.TAG.SPONSOR}:${process.env.PROJECT_SPONSOR || ""}`,
|
|
76
297
|
DD_TRACE_OTEL_ENABLED: "false",
|
|
77
298
|
};
|
|
78
299
|
// Add environment variables only if they don't already exist
|
|
@@ -82,8 +303,8 @@ function addDatadogLayers(lambdaFunction, options = {}) {
|
|
|
82
303
|
const datadogApiKeySecret = secretsmanager__namespace.Secret.fromSecretCompleteArn(lambdaFunction, "DatadogApiKey", resolvedDatadogApiKeyArn);
|
|
83
304
|
const datadogLambda = new datadogCdkConstructsV2.DatadogLambda(lambdaFunction, "DatadogLambda", {
|
|
84
305
|
apiKeySecret: datadogApiKeySecret, // apiKeySecret auto-grants secret access to the added lambdas
|
|
85
|
-
nodeLayerVersion:
|
|
86
|
-
extensionLayerVersion:
|
|
306
|
+
nodeLayerVersion: CDK$2.DATADOG.LAYER.NODE,
|
|
307
|
+
extensionLayerVersion: CDK$2.DATADOG.LAYER.EXTENSION,
|
|
87
308
|
env: process.env.PROJECT_ENV,
|
|
88
309
|
service: process.env.PROJECT_SERVICE,
|
|
89
310
|
version: process.env.PROJECT_VERSION,
|
|
@@ -135,35 +356,35 @@ function constructTagger(construct, { name } = {}) {
|
|
|
135
356
|
const stackName = name || constructStackName();
|
|
136
357
|
const version = process.env.npm_package_version || process.env.PROJECT_VERSION || null;
|
|
137
358
|
if (process.env.PROJECT_COMMIT && process.env.PROJECT_COMMIT.length > 8) {
|
|
138
|
-
cdk
|
|
359
|
+
cdk.Tags.of(construct).add(CDK$1.TAG.BUILD_HEX, process.env.PROJECT_COMMIT.slice(0, 8));
|
|
139
360
|
}
|
|
140
|
-
cdk
|
|
141
|
-
cdk
|
|
361
|
+
cdk.Tags.of(construct).add(CDK$1.TAG.BUILD_DATE, new Date().toISOString());
|
|
362
|
+
cdk.Tags.of(construct).add(CDK$1.TAG.BUILD_TIME, Date.now().toString());
|
|
142
363
|
if (process.env.PROJECT_COMMIT)
|
|
143
|
-
cdk
|
|
144
|
-
cdk
|
|
364
|
+
cdk.Tags.of(construct).add(CDK$1.TAG.COMMIT, process.env.PROJECT_COMMIT);
|
|
365
|
+
cdk.Tags.of(construct).add(CDK$1.TAG.CREATION, CDK$1.CREATION.CDK);
|
|
145
366
|
if (process.env.PROJECT_ENV)
|
|
146
|
-
cdk
|
|
367
|
+
cdk.Tags.of(construct).add(CDK$1.TAG.ENV, process.env.PROJECT_ENV);
|
|
147
368
|
if (process.env.PROJECT_NONCE)
|
|
148
|
-
cdk
|
|
369
|
+
cdk.Tags.of(construct).add(CDK$1.TAG.NONCE, process.env.PROJECT_NONCE);
|
|
149
370
|
if (process.env.PROJECT_KEY)
|
|
150
|
-
cdk
|
|
151
|
-
cdk
|
|
371
|
+
cdk.Tags.of(construct).add(CDK$1.TAG.PROJECT, process.env.PROJECT_KEY);
|
|
372
|
+
cdk.Tags.of(construct).add(CDK$1.TAG.ROLE, CDK$1.ROLE.STACK);
|
|
152
373
|
if (process.env.PROJECT_SERVICE)
|
|
153
|
-
cdk
|
|
374
|
+
cdk.Tags.of(construct).add(CDK$1.TAG.SERVICE, process.env.PROJECT_SERVICE);
|
|
154
375
|
if (process.env.PROJECT_SPONSOR)
|
|
155
|
-
cdk
|
|
376
|
+
cdk.Tags.of(construct).add(CDK$1.TAG.SPONSOR, process.env.PROJECT_SPONSOR);
|
|
156
377
|
if (stackName)
|
|
157
|
-
cdk
|
|
378
|
+
cdk.Tags.of(construct).add(CDK$1.TAG.STACK, stackName);
|
|
158
379
|
if (version)
|
|
159
|
-
cdk
|
|
380
|
+
cdk.Tags.of(construct).add(CDK$1.TAG.VERSION, version);
|
|
160
381
|
return true;
|
|
161
382
|
}
|
|
162
383
|
|
|
163
384
|
function envHostname({ component, domain, env, subdomain, } = {}) {
|
|
164
385
|
const resolvedDomain = domain || process.env.CDK_ENV_DOMAIN || process.env.CDK_ENV_HOSTED_ZONE;
|
|
165
386
|
if (!resolvedDomain) {
|
|
166
|
-
throw new
|
|
387
|
+
throw new errors.ConfigurationError("No hostname `domain` provided. Set CDK_ENV_DOMAIN or CDK_ENV_HOSTED_ZONE to use environment domain");
|
|
167
388
|
}
|
|
168
389
|
const resolvedComponent = component === "@" || component === "" ? undefined : component;
|
|
169
390
|
const resolvedSubdomain = subdomain || process.env.CDK_ENV_SUBDOMAIN;
|
|
@@ -177,6 +398,55 @@ function envHostname({ component, domain, env, subdomain, } = {}) {
|
|
|
177
398
|
return parts.join(".");
|
|
178
399
|
}
|
|
179
400
|
|
|
401
|
+
/**
|
|
402
|
+
* Extends the Datadog IAM role with additional permissions
|
|
403
|
+
*
|
|
404
|
+
* Checks for CDK_ENV_DATADOG_ROLE_ARN environment variable.
|
|
405
|
+
* If found, creates a custom policy with:
|
|
406
|
+
* - budgets:ViewBudget
|
|
407
|
+
* - logs:DescribeLogGroups
|
|
408
|
+
*
|
|
409
|
+
* @param scope - The construct scope
|
|
410
|
+
* @param options - Configuration options
|
|
411
|
+
* @returns The created Policy, or undefined if CDK_ENV_DATADOG_ROLE_ARN is not set
|
|
412
|
+
*/
|
|
413
|
+
function extendDatadogRole(scope, options) {
|
|
414
|
+
const datadogRoleArn = process.env.CDK_ENV_DATADOG_ROLE_ARN;
|
|
415
|
+
// Early return if no Datadog role ARN is configured
|
|
416
|
+
if (!datadogRoleArn) {
|
|
417
|
+
return undefined;
|
|
418
|
+
}
|
|
419
|
+
const { id = "DatadogCustomPolicy", project, service = CDK$2.SERVICE.DATADOG, } = options || {};
|
|
420
|
+
// Lookup the Datadog role
|
|
421
|
+
const datadogRole = awsIam.Role.fromRoleArn(scope, "DatadogRole", datadogRoleArn);
|
|
422
|
+
// Build policy statements
|
|
423
|
+
const statements = [
|
|
424
|
+
// Allow view budget
|
|
425
|
+
new awsIam.PolicyStatement({
|
|
426
|
+
actions: ["budgets:ViewBudget"],
|
|
427
|
+
resources: ["*"],
|
|
428
|
+
}),
|
|
429
|
+
// Allow describe log groups
|
|
430
|
+
new awsIam.PolicyStatement({
|
|
431
|
+
actions: ["logs:DescribeLogGroups"],
|
|
432
|
+
resources: ["*"],
|
|
433
|
+
}),
|
|
434
|
+
];
|
|
435
|
+
// Create the custom policy
|
|
436
|
+
const datadogCustomPolicy = new awsIam.Policy(scope, id, {
|
|
437
|
+
roles: [datadogRole],
|
|
438
|
+
statements,
|
|
439
|
+
});
|
|
440
|
+
// Add tags
|
|
441
|
+
cdk__namespace.Tags.of(datadogCustomPolicy).add(CDK$2.TAG.SERVICE, service);
|
|
442
|
+
cdk__namespace.Tags.of(datadogCustomPolicy).add(CDK$2.TAG.ROLE, CDK$2.ROLE.MONITORING);
|
|
443
|
+
cdk__namespace.Tags.of(datadogCustomPolicy).add(CDK$2.TAG.VENDOR, CDK$2.VENDOR.DATADOG);
|
|
444
|
+
if (project) {
|
|
445
|
+
cdk__namespace.Tags.of(datadogCustomPolicy).add(CDK$2.TAG.PROJECT, project);
|
|
446
|
+
}
|
|
447
|
+
return datadogCustomPolicy;
|
|
448
|
+
}
|
|
449
|
+
|
|
180
450
|
/**
|
|
181
451
|
* Check if the current environment matches the given environment
|
|
182
452
|
*/
|
|
@@ -187,13 +457,86 @@ function isEnv(env) {
|
|
|
187
457
|
* Check if the current environment is production
|
|
188
458
|
*/
|
|
189
459
|
function isProductionEnv() {
|
|
190
|
-
return isEnv(
|
|
460
|
+
return isEnv(CDK$2.ENV.PRODUCTION);
|
|
191
461
|
}
|
|
192
462
|
/**
|
|
193
463
|
* Check if the current environment is sandbox
|
|
194
464
|
*/
|
|
195
465
|
function isSandboxEnv() {
|
|
196
|
-
return isEnv(
|
|
466
|
+
return isEnv(CDK$2.ENV.SANDBOX);
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
// In short the RFC 1035 standard for valid hostnames is:
|
|
470
|
+
// 1. Must be less than 253 characters
|
|
471
|
+
// 2. Must start with a letter
|
|
472
|
+
// 3. Must end with a letter or number
|
|
473
|
+
// 4. Can only contain letters, numbers, and hyphens
|
|
474
|
+
// 5. Last part of the domain must be at least 2 characters
|
|
475
|
+
function validPart$1(part) {
|
|
476
|
+
if (!part.match(/^[a-z]/))
|
|
477
|
+
return false;
|
|
478
|
+
if (!part.match(/[a-z0-9]$/))
|
|
479
|
+
return false;
|
|
480
|
+
return /^[a-zA-Z0-9-]+$/.test(part);
|
|
481
|
+
}
|
|
482
|
+
function isValidHostname$1(hostname) {
|
|
483
|
+
// Check hostname is a string
|
|
484
|
+
if (typeof hostname !== "string")
|
|
485
|
+
return false;
|
|
486
|
+
// Convert hostname to lowercase
|
|
487
|
+
const check = hostname.toString().toLowerCase();
|
|
488
|
+
// Check hostname is less than 253 characters
|
|
489
|
+
if (check.length > 253)
|
|
490
|
+
return false;
|
|
491
|
+
// Split on dots
|
|
492
|
+
const parts = check.split(".");
|
|
493
|
+
// Check each part is validPart
|
|
494
|
+
const validParts = parts.map(validPart$1);
|
|
495
|
+
// Confirm all parts are valid
|
|
496
|
+
if (!validParts.every((part) => part))
|
|
497
|
+
return false;
|
|
498
|
+
// Confirm last part is at least 2 characters
|
|
499
|
+
const lastPart = parts[parts.length - 1];
|
|
500
|
+
if (lastPart.length < 2)
|
|
501
|
+
return false;
|
|
502
|
+
// Confirm last part is all letters
|
|
503
|
+
if (!lastPart.match(/^[a-z]+$/))
|
|
504
|
+
return false;
|
|
505
|
+
// This is a valid hostname
|
|
506
|
+
return true;
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
function validPart(part) {
|
|
510
|
+
if (!part.match(/^[a-z]/))
|
|
511
|
+
return false;
|
|
512
|
+
if (!part.match(/[a-z0-9]$/))
|
|
513
|
+
return false;
|
|
514
|
+
return /^[a-zA-Z0-9-]+$/.test(part);
|
|
515
|
+
}
|
|
516
|
+
function isValidSubdomain(subdomain) {
|
|
517
|
+
// Check subdomain is a string
|
|
518
|
+
if (typeof subdomain !== "string")
|
|
519
|
+
return false;
|
|
520
|
+
// Special case for apex
|
|
521
|
+
if (subdomain === CDK$2.HOST.APEX)
|
|
522
|
+
return true;
|
|
523
|
+
// Convert subdomain to lowercase
|
|
524
|
+
const check = subdomain.toString().toLowerCase();
|
|
525
|
+
// Check subdomain is less than 250 characters
|
|
526
|
+
// We use 250 instead of 253 because we need to leave room for the dot top-level domain
|
|
527
|
+
if (check.length > 250)
|
|
528
|
+
return false;
|
|
529
|
+
// Split on dots
|
|
530
|
+
const parts = check.split(".");
|
|
531
|
+
// Check each part is validPart
|
|
532
|
+
const validParts = parts.map(validPart);
|
|
533
|
+
// Confirm all parts are valid
|
|
534
|
+
if (!validParts.every((part) => part))
|
|
535
|
+
return false;
|
|
536
|
+
// Do not care if last part is at least 2 characters
|
|
537
|
+
// Do not care if last part is all letters
|
|
538
|
+
// This is a valid subdomain
|
|
539
|
+
return true;
|
|
197
540
|
}
|
|
198
541
|
|
|
199
542
|
function jaypieLambdaEnv(options = {}) {
|
|
@@ -250,6 +593,21 @@ function jaypieLambdaEnv(options = {}) {
|
|
|
250
593
|
return environment;
|
|
251
594
|
}
|
|
252
595
|
|
|
596
|
+
function mergeDomain(subDomain, hostedZone) {
|
|
597
|
+
if (!hostedZone) {
|
|
598
|
+
throw new errors.ConfigurationError("hostedZone is required");
|
|
599
|
+
}
|
|
600
|
+
if (!subDomain) {
|
|
601
|
+
// Return hostedZone if subDomain is not passed
|
|
602
|
+
// Pass CDK.HOST.APEX to explicitly indicate apex domain
|
|
603
|
+
return hostedZone;
|
|
604
|
+
}
|
|
605
|
+
if (subDomain === CDK$2.HOST.APEX) {
|
|
606
|
+
return hostedZone;
|
|
607
|
+
}
|
|
608
|
+
return `${subDomain}.${hostedZone}`;
|
|
609
|
+
}
|
|
610
|
+
|
|
253
611
|
const DEFAULT_FUNCTION_NAME$1 = "DatadogForwarderFunction";
|
|
254
612
|
// Cache to store resolved functions
|
|
255
613
|
// Using nested structure to support multiple functions per scope with automatic GC
|
|
@@ -257,7 +615,7 @@ const functionCache = new WeakMap();
|
|
|
257
615
|
function resolveDatadogForwarderFunction(scope, options) {
|
|
258
616
|
const { import: importValue, name } = options || {};
|
|
259
617
|
const functionName = name || DEFAULT_FUNCTION_NAME$1;
|
|
260
|
-
const importKey = importValue ||
|
|
618
|
+
const importKey = importValue || CDK$2.IMPORT.DATADOG_LOG_FORWARDER;
|
|
261
619
|
// Create a cache key based on name and import
|
|
262
620
|
const cacheKey = `${functionName}:${importKey}`;
|
|
263
621
|
// Get or create scope cache
|
|
@@ -279,7 +637,7 @@ function resolveDatadogForwarderFunction(scope, options) {
|
|
|
279
637
|
|
|
280
638
|
function resolveDatadogLayers(scope, options = {}) {
|
|
281
639
|
const { datadogApiKeyArn, uniqueId } = options;
|
|
282
|
-
let resolvedRegion = cdk
|
|
640
|
+
let resolvedRegion = cdk.Stack.of(scope).region || "us-east-1";
|
|
283
641
|
// Resolve the Datadog API key ARN from multiple sources
|
|
284
642
|
const resolvedDatadogApiKeyArn = datadogApiKeyArn ||
|
|
285
643
|
process.env.DATADOG_API_KEY_ARN ||
|
|
@@ -290,9 +648,9 @@ function resolveDatadogLayers(scope, options = {}) {
|
|
|
290
648
|
}
|
|
291
649
|
const layerIdSuffix = uniqueId || process.env.PROJECT_NONCE || Date.now().toString();
|
|
292
650
|
// Create Datadog Node.js layer
|
|
293
|
-
const datadogNodeLayer = lambda__namespace.LayerVersion.fromLayerVersionArn(scope, `DatadogNodeLayer-${layerIdSuffix}`, `arn:aws:lambda:${resolvedRegion}:464622532012:layer:Datadog-Node20-x:${
|
|
651
|
+
const datadogNodeLayer = lambda__namespace.LayerVersion.fromLayerVersionArn(scope, `DatadogNodeLayer-${layerIdSuffix}`, `arn:aws:lambda:${resolvedRegion}:464622532012:layer:Datadog-Node20-x:${CDK$2.DATADOG.LAYER.NODE}`);
|
|
294
652
|
// Create Datadog Extension layer
|
|
295
|
-
const datadogExtensionLayer = lambda__namespace.LayerVersion.fromLayerVersionArn(scope, `DatadogExtensionLayer-${layerIdSuffix}`, `arn:aws:lambda:${resolvedRegion}:464622532012:layer:Datadog-Extension:${
|
|
653
|
+
const datadogExtensionLayer = lambda__namespace.LayerVersion.fromLayerVersionArn(scope, `DatadogExtensionLayer-${layerIdSuffix}`, `arn:aws:lambda:${resolvedRegion}:464622532012:layer:Datadog-Extension:${CDK$2.DATADOG.LAYER.EXTENSION}`);
|
|
296
654
|
return [datadogNodeLayer, datadogExtensionLayer];
|
|
297
655
|
}
|
|
298
656
|
|
|
@@ -304,7 +662,7 @@ function resolveDatadogLoggingDestination(scope, options) {
|
|
|
304
662
|
const { import: importValue, name } = options || {};
|
|
305
663
|
// Create a cache key based on name and import (same as forwarder function)
|
|
306
664
|
const functionName = name || DEFAULT_FUNCTION_NAME;
|
|
307
|
-
const importKey = importValue ||
|
|
665
|
+
const importKey = importValue || CDK$2.IMPORT.DATADOG_LOG_FORWARDER;
|
|
308
666
|
const cacheKey = `${functionName}:${importKey}`;
|
|
309
667
|
// Get or create scope cache
|
|
310
668
|
let scopeCache = destinationCache.get(scope);
|
|
@@ -327,7 +685,7 @@ function resolveDatadogLoggingDestination(scope, options) {
|
|
|
327
685
|
|
|
328
686
|
function resolveHostedZone(scope, { name = "HostedZone", zone = process.env.CDK_ENV_HOSTED_ZONE, } = {}) {
|
|
329
687
|
if (!zone) {
|
|
330
|
-
throw new
|
|
688
|
+
throw new errors.ConfigurationError("No `zone` provided. Set CDK_ENV_HOSTED_ZONE to use environment zone");
|
|
331
689
|
}
|
|
332
690
|
if (typeof zone === "string") {
|
|
333
691
|
return route53__namespace.HostedZone.fromLookup(scope, name, {
|
|
@@ -360,7 +718,7 @@ const resolveParamsAndSecrets = ({ paramsAndSecrets, options, }) => {
|
|
|
360
718
|
class JaypieApiGateway extends constructs.Construct {
|
|
361
719
|
constructor(scope, id, props) {
|
|
362
720
|
super(scope, id);
|
|
363
|
-
const { certificate = true, handler, host: propsHost, name, roleTag =
|
|
721
|
+
const { certificate = true, handler, host: propsHost, name, roleTag = CDK$2.ROLE.API, zone: propsZone, } = props;
|
|
364
722
|
// Determine zone from props or environment
|
|
365
723
|
let zone = propsZone;
|
|
366
724
|
if (!zone && process.env.CDK_ENV_API_HOSTED_ZONE) {
|
|
@@ -374,7 +732,7 @@ class JaypieApiGateway extends constructs.Construct {
|
|
|
374
732
|
}
|
|
375
733
|
else if (process.env.CDK_ENV_API_SUBDOMAIN &&
|
|
376
734
|
process.env.CDK_ENV_API_HOSTED_ZONE) {
|
|
377
|
-
host =
|
|
735
|
+
host = mergeDomain(process.env.CDK_ENV_API_SUBDOMAIN, process.env.CDK_ENV_API_HOSTED_ZONE);
|
|
378
736
|
}
|
|
379
737
|
}
|
|
380
738
|
const apiGatewayName = name || constructEnvName("ApiGateway");
|
|
@@ -389,7 +747,7 @@ class JaypieApiGateway extends constructs.Construct {
|
|
|
389
747
|
domainName: host,
|
|
390
748
|
validation: acm__namespace.CertificateValidation.fromDns(hostedZone),
|
|
391
749
|
});
|
|
392
|
-
cdk
|
|
750
|
+
cdk.Tags.of(certificateToUse).add(CDK$2.TAG.ROLE, CDK$2.ROLE.HOSTING);
|
|
393
751
|
}
|
|
394
752
|
else if (typeof certificate === "object") {
|
|
395
753
|
certificateToUse = certificate;
|
|
@@ -408,19 +766,19 @@ class JaypieApiGateway extends constructs.Construct {
|
|
|
408
766
|
handler,
|
|
409
767
|
...lambdaRestApiProps,
|
|
410
768
|
});
|
|
411
|
-
cdk
|
|
769
|
+
cdk.Tags.of(this._api).add(CDK$2.TAG.ROLE, roleTag);
|
|
412
770
|
if (host && certificateToUse && hostedZone) {
|
|
413
771
|
this._domainName = this._api.addDomainName(apiDomainName, {
|
|
414
772
|
domainName: host,
|
|
415
773
|
certificate: certificateToUse,
|
|
416
774
|
});
|
|
417
|
-
cdk
|
|
775
|
+
cdk.Tags.of(this._domainName).add(CDK$2.TAG.ROLE, roleTag);
|
|
418
776
|
const record = new route53__namespace.ARecord(this, "AliasRecord", {
|
|
419
777
|
recordName: host,
|
|
420
778
|
target: route53__namespace.RecordTarget.fromAlias(new route53Targets__namespace.ApiGatewayDomain(this._domainName)),
|
|
421
779
|
zone: hostedZone,
|
|
422
780
|
});
|
|
423
|
-
cdk
|
|
781
|
+
cdk.Tags.of(record).add(CDK$2.TAG.ROLE, CDK$2.ROLE.NETWORKING);
|
|
424
782
|
}
|
|
425
783
|
}
|
|
426
784
|
get api() {
|
|
@@ -461,8 +819,8 @@ class JaypieApiGateway extends constructs.Construct {
|
|
|
461
819
|
}
|
|
462
820
|
get env() {
|
|
463
821
|
return {
|
|
464
|
-
account: cdk
|
|
465
|
-
region: cdk
|
|
822
|
+
account: cdk.Stack.of(this).account,
|
|
823
|
+
region: cdk.Stack.of(this).region,
|
|
466
824
|
};
|
|
467
825
|
}
|
|
468
826
|
get stack() {
|
|
@@ -505,7 +863,7 @@ class JaypieApiGateway extends constructs.Construct {
|
|
|
505
863
|
}
|
|
506
864
|
}
|
|
507
865
|
|
|
508
|
-
class JaypieStack extends cdk
|
|
866
|
+
class JaypieStack extends cdk.Stack {
|
|
509
867
|
constructor(scope, id, props = {}) {
|
|
510
868
|
const { key, ...stackProps } = props;
|
|
511
869
|
// Handle stackName
|
|
@@ -538,7 +896,7 @@ class JaypieAppStack extends JaypieStack {
|
|
|
538
896
|
class JaypieLambda extends constructs.Construct {
|
|
539
897
|
constructor(scope, id, props) {
|
|
540
898
|
super(scope, id);
|
|
541
|
-
const { allowAllOutbound, allowPublicSubnet, architecture = lambda__namespace.Architecture.X86_64, code, datadogApiKeyArn, deadLetterQueue, deadLetterQueueEnabled, deadLetterTopic, description, environment: initialEnvironment = {}, envSecrets = {}, ephemeralStorageSize, filesystem, handler = "index.handler", initialPolicy, layers = [], logRetention =
|
|
899
|
+
const { allowAllOutbound, allowPublicSubnet, architecture = lambda__namespace.Architecture.X86_64, code, datadogApiKeyArn, deadLetterQueue, deadLetterQueueEnabled, deadLetterTopic, description, environment: initialEnvironment = {}, envSecrets = {}, ephemeralStorageSize, filesystem, handler = "index.handler", initialPolicy, layers = [], logRetention = CDK$2.LAMBDA.LOG_RETENTION, logRetentionRole, logRetentionRetryOptions, maxEventAge, memorySize = CDK$2.LAMBDA.MEMORY_SIZE, paramsAndSecrets, paramsAndSecretsOptions, profiling, profilingGroup, provisionedConcurrentExecutions, reservedConcurrentExecutions, retryAttempts, roleTag = CDK$2.ROLE.PROCESSING, runtime = lambda__namespace.Runtime.NODEJS_22_X, runtimeManagementMode, secrets = [], securityGroups, timeout = cdk.Duration.seconds(CDK$2.DURATION.LAMBDA_WORKER), tracing, vendorTag, vpc, vpcSubnets, } = props;
|
|
542
900
|
// Get base environment with defaults
|
|
543
901
|
const environment = jaypieLambdaEnv({ initialEnvironment });
|
|
544
902
|
const codeAsset = typeof code === "string" ? lambda__namespace.Code.fromAsset(code) : code;
|
|
@@ -597,14 +955,14 @@ class JaypieLambda extends constructs.Construct {
|
|
|
597
955
|
runtime,
|
|
598
956
|
runtimeManagementMode,
|
|
599
957
|
securityGroups,
|
|
600
|
-
timeout: typeof timeout === "number" ? cdk
|
|
958
|
+
timeout: typeof timeout === "number" ? cdk.Duration.seconds(timeout) : timeout,
|
|
601
959
|
tracing,
|
|
602
960
|
vpc,
|
|
603
961
|
vpcSubnets,
|
|
604
962
|
// Enable auto-publishing of versions when using provisioned concurrency
|
|
605
963
|
currentVersionOptions: provisionedConcurrentExecutions !== undefined
|
|
606
964
|
? {
|
|
607
|
-
removalPolicy: cdk
|
|
965
|
+
removalPolicy: cdk.RemovalPolicy.RETAIN,
|
|
608
966
|
description: "Auto-published version for provisioned concurrency",
|
|
609
967
|
// Don't set provisioned concurrency here - it will be set on the alias
|
|
610
968
|
}
|
|
@@ -633,10 +991,10 @@ class JaypieLambda extends constructs.Construct {
|
|
|
633
991
|
this._provisioned.node.addDependency(version);
|
|
634
992
|
}
|
|
635
993
|
if (roleTag) {
|
|
636
|
-
cdk
|
|
994
|
+
cdk.Tags.of(this._lambda).add(CDK$2.TAG.ROLE, roleTag);
|
|
637
995
|
}
|
|
638
996
|
if (vendorTag) {
|
|
639
|
-
cdk
|
|
997
|
+
cdk.Tags.of(this._lambda).add(CDK$2.TAG.VENDOR, vendorTag);
|
|
640
998
|
}
|
|
641
999
|
// Assign _reference based on provisioned state
|
|
642
1000
|
this._reference =
|
|
@@ -739,8 +1097,8 @@ class JaypieLambda extends constructs.Construct {
|
|
|
739
1097
|
}
|
|
740
1098
|
get env() {
|
|
741
1099
|
return {
|
|
742
|
-
account: cdk
|
|
743
|
-
region: cdk
|
|
1100
|
+
account: cdk.Stack.of(this).account,
|
|
1101
|
+
region: cdk.Stack.of(this).region,
|
|
744
1102
|
};
|
|
745
1103
|
}
|
|
746
1104
|
get stack() {
|
|
@@ -757,19 +1115,19 @@ class JaypieLambda extends constructs.Construct {
|
|
|
757
1115
|
class JaypieQueuedLambda extends constructs.Construct {
|
|
758
1116
|
constructor(scope, id, props) {
|
|
759
1117
|
super(scope, id);
|
|
760
|
-
const { allowAllOutbound, allowPublicSubnet, architecture, batchSize = 1, code, datadogApiKeyArn, deadLetterQueue, deadLetterQueueEnabled, deadLetterTopic, description, environment = {}, envSecrets = {}, ephemeralStorageSize, fifo = true, filesystem, handler = "index.handler", initialPolicy, layers = [], logRetention =
|
|
1118
|
+
const { allowAllOutbound, allowPublicSubnet, architecture, batchSize = 1, code, datadogApiKeyArn, deadLetterQueue, deadLetterQueueEnabled, deadLetterTopic, description, environment = {}, envSecrets = {}, ephemeralStorageSize, fifo = true, filesystem, handler = "index.handler", initialPolicy, layers = [], logRetention = CDK$2.LAMBDA.LOG_RETENTION, logRetentionRole, logRetentionRetryOptions, maxEventAge, memorySize = CDK$2.LAMBDA.MEMORY_SIZE, paramsAndSecrets, paramsAndSecretsOptions, profiling, profilingGroup, provisionedConcurrentExecutions, reservedConcurrentExecutions, retryAttempts, roleTag, runtime = lambda__namespace.Runtime.NODEJS_22_X, runtimeManagementMode, secrets = [], securityGroups, timeout = cdk.Duration.seconds(CDK$2.DURATION.LAMBDA_WORKER), tracing, vendorTag, visibilityTimeout = cdk.Duration.seconds(CDK$2.DURATION.LAMBDA_WORKER), vpc, vpcSubnets, } = props;
|
|
761
1119
|
// Create SQS Queue
|
|
762
1120
|
this._queue = new sqs__namespace.Queue(this, "Queue", {
|
|
763
1121
|
fifo,
|
|
764
1122
|
visibilityTimeout: typeof visibilityTimeout === "number"
|
|
765
|
-
? cdk
|
|
1123
|
+
? cdk.Duration.seconds(visibilityTimeout)
|
|
766
1124
|
: visibilityTimeout,
|
|
767
1125
|
});
|
|
768
1126
|
if (roleTag) {
|
|
769
|
-
cdk
|
|
1127
|
+
cdk.Tags.of(this._queue).add(CDK$2.TAG.ROLE, roleTag);
|
|
770
1128
|
}
|
|
771
1129
|
if (vendorTag) {
|
|
772
|
-
cdk
|
|
1130
|
+
cdk.Tags.of(this._queue).add(CDK$2.TAG.VENDOR, vendorTag);
|
|
773
1131
|
}
|
|
774
1132
|
// Create Lambda with JaypieLambda
|
|
775
1133
|
this._lambdaConstruct = new JaypieLambda(this, "Function", {
|
|
@@ -914,12 +1272,12 @@ class JaypieQueuedLambda extends constructs.Construct {
|
|
|
914
1272
|
}
|
|
915
1273
|
get env() {
|
|
916
1274
|
return {
|
|
917
|
-
account: cdk
|
|
918
|
-
region: cdk
|
|
1275
|
+
account: cdk.Stack.of(this).account,
|
|
1276
|
+
region: cdk.Stack.of(this).region,
|
|
919
1277
|
};
|
|
920
1278
|
}
|
|
921
1279
|
get stack() {
|
|
922
|
-
return cdk
|
|
1280
|
+
return cdk.Stack.of(this);
|
|
923
1281
|
}
|
|
924
1282
|
applyRemovalPolicy(policy) {
|
|
925
1283
|
this._lambdaConstruct.applyRemovalPolicy(policy);
|
|
@@ -997,15 +1355,15 @@ class JaypieBucketQueuedLambda extends JaypieQueuedLambda {
|
|
|
997
1355
|
// Create S3 Bucket
|
|
998
1356
|
this._bucket = new s3__namespace.Bucket(this, "Bucket", {
|
|
999
1357
|
bucketName: bucketOptions.bucketName || bucketName,
|
|
1000
|
-
removalPolicy: bucketOptions.removalPolicy || cdk
|
|
1358
|
+
removalPolicy: bucketOptions.removalPolicy || cdk.RemovalPolicy.RETAIN,
|
|
1001
1359
|
...bucketOptions,
|
|
1002
1360
|
});
|
|
1003
1361
|
// Add tags to bucket
|
|
1004
1362
|
if (roleTag) {
|
|
1005
|
-
cdk
|
|
1363
|
+
cdk.Tags.of(this._bucket).add(CDK$2.TAG.ROLE, roleTag);
|
|
1006
1364
|
}
|
|
1007
1365
|
if (vendorTag) {
|
|
1008
|
-
cdk
|
|
1366
|
+
cdk.Tags.of(this._bucket).add(CDK$2.TAG.VENDOR, vendorTag);
|
|
1009
1367
|
}
|
|
1010
1368
|
// Add an event notification from the bucket to the queue
|
|
1011
1369
|
this._bucket.addEventNotification(s3__namespace.EventType.OBJECT_CREATED, new s3n__namespace.SqsDestination(this.queue));
|
|
@@ -1128,15 +1486,184 @@ class JaypieBucketQueuedLambda extends JaypieQueuedLambda {
|
|
|
1128
1486
|
}
|
|
1129
1487
|
}
|
|
1130
1488
|
|
|
1489
|
+
class JaypieDatadogBucket extends constructs.Construct {
|
|
1490
|
+
/**
|
|
1491
|
+
* Create a new S3 bucket for Datadog log archiving with automatic IAM permissions
|
|
1492
|
+
*/
|
|
1493
|
+
constructor(scope, idOrProps, propsOrUndefined) {
|
|
1494
|
+
// Handle overloaded constructor signatures
|
|
1495
|
+
let props;
|
|
1496
|
+
let id;
|
|
1497
|
+
if (typeof idOrProps === "string") {
|
|
1498
|
+
// First param is ID, second is props
|
|
1499
|
+
props = propsOrUndefined || {};
|
|
1500
|
+
id = idOrProps;
|
|
1501
|
+
}
|
|
1502
|
+
else {
|
|
1503
|
+
// First param is props
|
|
1504
|
+
props = idOrProps || {};
|
|
1505
|
+
id = props.id || "DatadogArchiveBucket";
|
|
1506
|
+
}
|
|
1507
|
+
super(scope, id);
|
|
1508
|
+
// Extract Jaypie-specific options
|
|
1509
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
1510
|
+
const { grantDatadogAccess = true, id: _id, project, service = CDK$2.SERVICE.DATADOG, ...bucketProps } = props;
|
|
1511
|
+
// Create the bucket
|
|
1512
|
+
this.bucket = new s3.Bucket(this, "Bucket", bucketProps);
|
|
1513
|
+
// Add tags to bucket
|
|
1514
|
+
cdk__namespace.Tags.of(this.bucket).add(CDK$2.TAG.SERVICE, service);
|
|
1515
|
+
cdk__namespace.Tags.of(this.bucket).add(CDK$2.TAG.ROLE, CDK$2.ROLE.MONITORING);
|
|
1516
|
+
if (project) {
|
|
1517
|
+
cdk__namespace.Tags.of(this.bucket).add(CDK$2.TAG.PROJECT, project);
|
|
1518
|
+
}
|
|
1519
|
+
// Grant Datadog role access to bucket if enabled
|
|
1520
|
+
if (grantDatadogAccess) {
|
|
1521
|
+
this.policy = this.grantDatadogRoleBucketAccess({ project, service });
|
|
1522
|
+
}
|
|
1523
|
+
}
|
|
1524
|
+
/**
|
|
1525
|
+
* Grants the Datadog IAM role access to this bucket
|
|
1526
|
+
*
|
|
1527
|
+
* Checks for CDK_ENV_DATADOG_ROLE_ARN environment variable.
|
|
1528
|
+
* If found, creates a custom policy with:
|
|
1529
|
+
* - s3:ListBucket on bucket
|
|
1530
|
+
* - s3:GetObject and s3:PutObject on bucket/*
|
|
1531
|
+
*
|
|
1532
|
+
* @param options - Configuration options
|
|
1533
|
+
* @returns The created Policy, or undefined if CDK_ENV_DATADOG_ROLE_ARN is not set
|
|
1534
|
+
*/
|
|
1535
|
+
grantDatadogRoleBucketAccess(options) {
|
|
1536
|
+
const datadogRoleArn = process.env.CDK_ENV_DATADOG_ROLE_ARN;
|
|
1537
|
+
// Early return if no Datadog role ARN is configured
|
|
1538
|
+
if (!datadogRoleArn) {
|
|
1539
|
+
return undefined;
|
|
1540
|
+
}
|
|
1541
|
+
const { project, service = CDK$2.SERVICE.DATADOG } = options || {};
|
|
1542
|
+
// Lookup the Datadog role
|
|
1543
|
+
const datadogRole = awsIam.Role.fromRoleArn(this, "DatadogRole", datadogRoleArn);
|
|
1544
|
+
// Build policy statements for bucket access
|
|
1545
|
+
const statements = [
|
|
1546
|
+
// Allow list bucket
|
|
1547
|
+
new awsIam.PolicyStatement({
|
|
1548
|
+
actions: ["s3:ListBucket"],
|
|
1549
|
+
resources: [this.bucket.bucketArn],
|
|
1550
|
+
}),
|
|
1551
|
+
// Allow read and write to the bucket
|
|
1552
|
+
new awsIam.PolicyStatement({
|
|
1553
|
+
actions: ["s3:GetObject", "s3:PutObject"],
|
|
1554
|
+
resources: [`${this.bucket.bucketArn}/*`],
|
|
1555
|
+
}),
|
|
1556
|
+
];
|
|
1557
|
+
// Create the custom policy
|
|
1558
|
+
const datadogBucketPolicy = new awsIam.Policy(this, "DatadogBucketPolicy", {
|
|
1559
|
+
roles: [datadogRole],
|
|
1560
|
+
statements,
|
|
1561
|
+
});
|
|
1562
|
+
// Add tags
|
|
1563
|
+
cdk__namespace.Tags.of(datadogBucketPolicy).add(CDK$2.TAG.SERVICE, service);
|
|
1564
|
+
cdk__namespace.Tags.of(datadogBucketPolicy).add(CDK$2.TAG.ROLE, CDK$2.ROLE.MONITORING);
|
|
1565
|
+
cdk__namespace.Tags.of(datadogBucketPolicy).add(CDK$2.TAG.VENDOR, CDK$2.VENDOR.DATADOG);
|
|
1566
|
+
if (project) {
|
|
1567
|
+
cdk__namespace.Tags.of(datadogBucketPolicy).add(CDK$2.TAG.PROJECT, project);
|
|
1568
|
+
}
|
|
1569
|
+
return datadogBucketPolicy;
|
|
1570
|
+
}
|
|
1571
|
+
}
|
|
1572
|
+
|
|
1573
|
+
const DATADOG_FORWARDER_TEMPLATE_URL = "https://datadog-cloudformation-template.s3.amazonaws.com/aws/forwarder/latest.yaml";
|
|
1574
|
+
const DEFAULT_RESERVED_CONCURRENCY = "10";
|
|
1575
|
+
class JaypieDatadogForwarder extends constructs.Construct {
|
|
1576
|
+
/**
|
|
1577
|
+
* Create a new Datadog forwarder with CloudFormation nested stack
|
|
1578
|
+
*/
|
|
1579
|
+
constructor(scope, idOrProps, propsOrUndefined) {
|
|
1580
|
+
// Handle overloaded constructor signatures
|
|
1581
|
+
let props;
|
|
1582
|
+
let id;
|
|
1583
|
+
if (typeof idOrProps === "string") {
|
|
1584
|
+
// First param is ID, second is props
|
|
1585
|
+
props = propsOrUndefined || {};
|
|
1586
|
+
id = idOrProps;
|
|
1587
|
+
}
|
|
1588
|
+
else {
|
|
1589
|
+
// First param is props
|
|
1590
|
+
props = idOrProps || {};
|
|
1591
|
+
id = props.id || "DatadogForwarder";
|
|
1592
|
+
}
|
|
1593
|
+
super(scope, id);
|
|
1594
|
+
// Resolve options with defaults
|
|
1595
|
+
const { account = process.env.CDK_ENV_ACCOUNT, additionalTags, createOutput = true, datadogApiKey = process.env.CDK_ENV_DATADOG_API_KEY, enableCloudFormationEvents = true, enableRoleExtension = true, exportName = CDK$2.IMPORT.DATADOG_LOG_FORWARDER, project, reservedConcurrency = DEFAULT_RESERVED_CONCURRENCY, service = CDK$2.VENDOR.DATADOG, templateUrl = DATADOG_FORWARDER_TEMPLATE_URL, } = props;
|
|
1596
|
+
// Validate required parameters
|
|
1597
|
+
if (!datadogApiKey) {
|
|
1598
|
+
throw new Error("Datadog API key is required. Provide via datadogApiKey prop or CDK_ENV_DATADOG_API_KEY environment variable.");
|
|
1599
|
+
}
|
|
1600
|
+
// Build Datadog tags
|
|
1601
|
+
let ddTags = account ? `account:${account}` : "";
|
|
1602
|
+
if (additionalTags) {
|
|
1603
|
+
ddTags = ddTags ? `${ddTags},${additionalTags}` : additionalTags;
|
|
1604
|
+
}
|
|
1605
|
+
// Deploy Datadog CloudFormation stack
|
|
1606
|
+
this.cfnStack = new cdk.CfnStack(this, "Stack", {
|
|
1607
|
+
parameters: {
|
|
1608
|
+
DdApiKey: datadogApiKey,
|
|
1609
|
+
DdTags: ddTags,
|
|
1610
|
+
ReservedConcurrency: reservedConcurrency,
|
|
1611
|
+
},
|
|
1612
|
+
templateUrl,
|
|
1613
|
+
});
|
|
1614
|
+
// Add tags to stack
|
|
1615
|
+
cdk__namespace.Tags.of(this.cfnStack).add(CDK$2.TAG.ROLE, CDK$2.ROLE.MONITORING);
|
|
1616
|
+
cdk__namespace.Tags.of(this.cfnStack).add(CDK$2.TAG.SERVICE, service);
|
|
1617
|
+
cdk__namespace.Tags.of(this.cfnStack).add(CDK$2.TAG.VENDOR, CDK$2.VENDOR.DATADOG);
|
|
1618
|
+
if (project) {
|
|
1619
|
+
cdk__namespace.Tags.of(this.cfnStack).add(CDK$2.TAG.PROJECT, project);
|
|
1620
|
+
}
|
|
1621
|
+
// Extract forwarder function from stack outputs
|
|
1622
|
+
this.forwarderFunction = lambda__namespace.Function.fromFunctionArn(this, "Function", this.cfnStack.getAtt("Outputs.DatadogForwarderArn").toString());
|
|
1623
|
+
// Extend Datadog role with custom permissions if enabled
|
|
1624
|
+
if (enableRoleExtension) {
|
|
1625
|
+
extendDatadogRole(this, { project, service });
|
|
1626
|
+
}
|
|
1627
|
+
// Create CloudFormation events rule if enabled
|
|
1628
|
+
if (enableCloudFormationEvents) {
|
|
1629
|
+
this.eventsRule = new awsEvents.Rule(this, "CloudFormationEventsRule", {
|
|
1630
|
+
eventPattern: {
|
|
1631
|
+
source: ["aws.cloudformation"],
|
|
1632
|
+
},
|
|
1633
|
+
targets: [
|
|
1634
|
+
new awsEventsTargets.LambdaFunction(this.forwarderFunction, {
|
|
1635
|
+
event: awsEvents.RuleTargetInput.fromEventPath("$"),
|
|
1636
|
+
}),
|
|
1637
|
+
],
|
|
1638
|
+
});
|
|
1639
|
+
// Add tags to events rule
|
|
1640
|
+
cdk__namespace.Tags.of(this.eventsRule).add(CDK$2.TAG.ROLE, CDK$2.ROLE.MONITORING);
|
|
1641
|
+
cdk__namespace.Tags.of(this.eventsRule).add(CDK$2.TAG.SERVICE, service);
|
|
1642
|
+
cdk__namespace.Tags.of(this.eventsRule).add(CDK$2.TAG.VENDOR, CDK$2.VENDOR.DATADOG);
|
|
1643
|
+
if (project) {
|
|
1644
|
+
cdk__namespace.Tags.of(this.eventsRule).add(CDK$2.TAG.PROJECT, project);
|
|
1645
|
+
}
|
|
1646
|
+
}
|
|
1647
|
+
// Create CloudFormation output if enabled
|
|
1648
|
+
if (createOutput) {
|
|
1649
|
+
new cdk__namespace.CfnOutput(this, "ForwarderArnOutput", {
|
|
1650
|
+
description: "Datadog Log Forwarder Lambda ARN",
|
|
1651
|
+
exportName,
|
|
1652
|
+
value: this.cfnStack.getAtt("Outputs.DatadogForwarderArn").toString(),
|
|
1653
|
+
});
|
|
1654
|
+
}
|
|
1655
|
+
}
|
|
1656
|
+
}
|
|
1657
|
+
|
|
1131
1658
|
// It is a consumer if the environment is ephemeral
|
|
1132
1659
|
function checkEnvIsConsumer(env = process.env) {
|
|
1133
|
-
return (env.PROJECT_ENV ===
|
|
1660
|
+
return (env.PROJECT_ENV === CDK$2.ENV.PERSONAL ||
|
|
1134
1661
|
!!env.CDK_ENV_PERSONAL ||
|
|
1135
1662
|
/** @deprecated */ env.PROJECT_ENV === "ephemeral" ||
|
|
1136
1663
|
/** @deprecated */ !!env.CDK_ENV_EPHEMERAL);
|
|
1137
1664
|
}
|
|
1138
1665
|
function checkEnvIsProvider(env = process.env) {
|
|
1139
|
-
return env.PROJECT_ENV ===
|
|
1666
|
+
return env.PROJECT_ENV === CDK$2.ENV.SANDBOX;
|
|
1140
1667
|
}
|
|
1141
1668
|
function cleanName(name) {
|
|
1142
1669
|
return name.replace(/[^a-zA-Z0-9:-]/g, "");
|
|
@@ -1150,7 +1677,7 @@ function exportEnvName(name, env = process.env) {
|
|
|
1150
1677
|
}
|
|
1151
1678
|
else {
|
|
1152
1679
|
if (checkEnvIsConsumer(env)) {
|
|
1153
|
-
rawName = `env-${
|
|
1680
|
+
rawName = `env-${CDK$2.ENV.SANDBOX}-${env.PROJECT_KEY}-${name}`;
|
|
1154
1681
|
}
|
|
1155
1682
|
else {
|
|
1156
1683
|
rawName = `env-${env.PROJECT_ENV}-${env.PROJECT_KEY}-${name}`;
|
|
@@ -1171,10 +1698,10 @@ class JaypieEnvSecret extends constructs.Construct {
|
|
|
1171
1698
|
exportName = cleanName(exportParam);
|
|
1172
1699
|
}
|
|
1173
1700
|
if (consumer) {
|
|
1174
|
-
const secretName = cdk
|
|
1701
|
+
const secretName = cdk.Fn.importValue(exportName);
|
|
1175
1702
|
this._secret = secretsmanager__namespace.Secret.fromSecretNameV2(this, id, secretName);
|
|
1176
1703
|
// Add CfnOutput for consumer secrets
|
|
1177
|
-
new cdk
|
|
1704
|
+
new cdk.CfnOutput(this, `ConsumedName`, {
|
|
1178
1705
|
value: this._secret.secretName,
|
|
1179
1706
|
});
|
|
1180
1707
|
}
|
|
@@ -1183,24 +1710,24 @@ class JaypieEnvSecret extends constructs.Construct {
|
|
|
1183
1710
|
const secretProps = {
|
|
1184
1711
|
generateSecretString,
|
|
1185
1712
|
secretStringValue: !generateSecretString && secretValue
|
|
1186
|
-
? cdk
|
|
1713
|
+
? cdk.SecretValue.unsafePlainText(secretValue)
|
|
1187
1714
|
: undefined,
|
|
1188
1715
|
};
|
|
1189
1716
|
this._secret = new secretsmanager__namespace.Secret(this, id, secretProps);
|
|
1190
1717
|
if (roleTag) {
|
|
1191
|
-
cdk
|
|
1718
|
+
cdk.Tags.of(this._secret).add(CDK$2.TAG.ROLE, roleTag);
|
|
1192
1719
|
}
|
|
1193
1720
|
if (vendorTag) {
|
|
1194
|
-
cdk
|
|
1721
|
+
cdk.Tags.of(this._secret).add(CDK$2.TAG.VENDOR, vendorTag);
|
|
1195
1722
|
}
|
|
1196
1723
|
if (provider) {
|
|
1197
|
-
new cdk
|
|
1724
|
+
new cdk.CfnOutput(this, `ProvidedName`, {
|
|
1198
1725
|
value: this._secret.secretName,
|
|
1199
1726
|
exportName,
|
|
1200
1727
|
});
|
|
1201
1728
|
}
|
|
1202
1729
|
else {
|
|
1203
|
-
new cdk
|
|
1730
|
+
new cdk.CfnOutput(this, `CreatedName`, {
|
|
1204
1731
|
value: this._secret.secretName,
|
|
1205
1732
|
});
|
|
1206
1733
|
}
|
|
@@ -1208,12 +1735,12 @@ class JaypieEnvSecret extends constructs.Construct {
|
|
|
1208
1735
|
}
|
|
1209
1736
|
// IResource implementation
|
|
1210
1737
|
get stack() {
|
|
1211
|
-
return cdk
|
|
1738
|
+
return cdk.Stack.of(this);
|
|
1212
1739
|
}
|
|
1213
1740
|
get env() {
|
|
1214
1741
|
return {
|
|
1215
|
-
account: cdk
|
|
1216
|
-
region: cdk
|
|
1742
|
+
account: cdk.Stack.of(this).account,
|
|
1743
|
+
region: cdk.Stack.of(this).region,
|
|
1217
1744
|
};
|
|
1218
1745
|
}
|
|
1219
1746
|
applyRemovalPolicy(policy) {
|
|
@@ -1265,8 +1792,8 @@ class JaypieDatadogSecret extends JaypieEnvSecret {
|
|
|
1265
1792
|
constructor(scope, id = "MongoConnectionString", props) {
|
|
1266
1793
|
const defaultProps = {
|
|
1267
1794
|
envKey: "DATADOG_API_KEY",
|
|
1268
|
-
roleTag:
|
|
1269
|
-
vendorTag:
|
|
1795
|
+
roleTag: CDK$2.ROLE.MONITORING,
|
|
1796
|
+
vendorTag: CDK$2.VENDOR.DATADOG,
|
|
1270
1797
|
...props,
|
|
1271
1798
|
};
|
|
1272
1799
|
super(scope, id, defaultProps);
|
|
@@ -1277,7 +1804,7 @@ class JaypieDnsRecord extends constructs.Construct {
|
|
|
1277
1804
|
constructor(scope, id, props) {
|
|
1278
1805
|
super(scope, id);
|
|
1279
1806
|
const { comment, recordName, type, values } = props;
|
|
1280
|
-
const ttl = props.ttl || cdk__namespace.Duration.seconds(
|
|
1807
|
+
const ttl = props.ttl || cdk__namespace.Duration.seconds(CDK$2.DNS.CONFIG.TTL);
|
|
1281
1808
|
// Resolve the hosted zone (supports both string and IHostedZone)
|
|
1282
1809
|
const zone = resolveHostedZone(scope, {
|
|
1283
1810
|
name: `${id}HostedZone`,
|
|
@@ -1292,9 +1819,9 @@ class JaypieDnsRecord extends constructs.Construct {
|
|
|
1292
1819
|
};
|
|
1293
1820
|
// Create the appropriate record based on type
|
|
1294
1821
|
switch (type) {
|
|
1295
|
-
case
|
|
1822
|
+
case CDK$2.DNS.RECORD.A: {
|
|
1296
1823
|
if (!Array.isArray(values) || values.length === 0) {
|
|
1297
|
-
throw new
|
|
1824
|
+
throw new errors.ConfigurationError("A record requires at least one IP address");
|
|
1298
1825
|
}
|
|
1299
1826
|
this.record = new route53.ARecord(this, "Record", {
|
|
1300
1827
|
...baseProps,
|
|
@@ -1302,9 +1829,9 @@ class JaypieDnsRecord extends constructs.Construct {
|
|
|
1302
1829
|
});
|
|
1303
1830
|
break;
|
|
1304
1831
|
}
|
|
1305
|
-
case
|
|
1832
|
+
case CDK$2.DNS.RECORD.CNAME: {
|
|
1306
1833
|
if (!Array.isArray(values) || values.length === 0) {
|
|
1307
|
-
throw new
|
|
1834
|
+
throw new errors.ConfigurationError("CNAME record requires a domain name");
|
|
1308
1835
|
}
|
|
1309
1836
|
this.record = new route53.CnameRecord(this, "Record", {
|
|
1310
1837
|
...baseProps,
|
|
@@ -1312,9 +1839,9 @@ class JaypieDnsRecord extends constructs.Construct {
|
|
|
1312
1839
|
});
|
|
1313
1840
|
break;
|
|
1314
1841
|
}
|
|
1315
|
-
case
|
|
1842
|
+
case CDK$2.DNS.RECORD.MX: {
|
|
1316
1843
|
if (!Array.isArray(values) || values.length === 0) {
|
|
1317
|
-
throw new
|
|
1844
|
+
throw new errors.ConfigurationError("MX record requires at least one mail server");
|
|
1318
1845
|
}
|
|
1319
1846
|
this.record = new route53.MxRecord(this, "Record", {
|
|
1320
1847
|
...baseProps,
|
|
@@ -1322,9 +1849,9 @@ class JaypieDnsRecord extends constructs.Construct {
|
|
|
1322
1849
|
});
|
|
1323
1850
|
break;
|
|
1324
1851
|
}
|
|
1325
|
-
case
|
|
1852
|
+
case CDK$2.DNS.RECORD.NS: {
|
|
1326
1853
|
if (!Array.isArray(values) || values.length === 0) {
|
|
1327
|
-
throw new
|
|
1854
|
+
throw new errors.ConfigurationError("NS record requires at least one name server");
|
|
1328
1855
|
}
|
|
1329
1856
|
this.record = new route53.NsRecord(this, "Record", {
|
|
1330
1857
|
...baseProps,
|
|
@@ -1332,9 +1859,9 @@ class JaypieDnsRecord extends constructs.Construct {
|
|
|
1332
1859
|
});
|
|
1333
1860
|
break;
|
|
1334
1861
|
}
|
|
1335
|
-
case
|
|
1862
|
+
case CDK$2.DNS.RECORD.TXT: {
|
|
1336
1863
|
if (!Array.isArray(values) || values.length === 0) {
|
|
1337
|
-
throw new
|
|
1864
|
+
throw new errors.ConfigurationError("TXT record requires at least one value");
|
|
1338
1865
|
}
|
|
1339
1866
|
this.record = new route53.TxtRecord(this, "Record", {
|
|
1340
1867
|
...baseProps,
|
|
@@ -1343,26 +1870,106 @@ class JaypieDnsRecord extends constructs.Construct {
|
|
|
1343
1870
|
break;
|
|
1344
1871
|
}
|
|
1345
1872
|
default:
|
|
1346
|
-
throw new
|
|
1873
|
+
throw new errors.ConfigurationError(`Unsupported DNS record type: ${type}. Supported types: A, CNAME, MX, NS, TXT`);
|
|
1347
1874
|
}
|
|
1348
1875
|
// Add standard tags to the DNS record
|
|
1349
|
-
cdk__namespace.Tags.of(this.record).add(
|
|
1350
|
-
cdk__namespace.Tags.of(this.record).add(
|
|
1876
|
+
cdk__namespace.Tags.of(this.record).add(CDK$2.TAG.SERVICE, CDK$2.SERVICE.INFRASTRUCTURE);
|
|
1877
|
+
cdk__namespace.Tags.of(this.record).add(CDK$2.TAG.ROLE, CDK$2.ROLE.NETWORKING);
|
|
1878
|
+
}
|
|
1879
|
+
}
|
|
1880
|
+
|
|
1881
|
+
class JaypieEventsRule extends constructs.Construct {
|
|
1882
|
+
/**
|
|
1883
|
+
* Create a new EventBridge rule that targets a Lambda function
|
|
1884
|
+
*/
|
|
1885
|
+
constructor(scope, idOrSourceOrProps, propsOrUndefined) {
|
|
1886
|
+
// Handle overloaded constructor signatures
|
|
1887
|
+
let props;
|
|
1888
|
+
let id;
|
|
1889
|
+
if (typeof idOrSourceOrProps === "string") {
|
|
1890
|
+
// Check if it looks like an AWS source (starts with "aws.")
|
|
1891
|
+
if (idOrSourceOrProps.startsWith("aws.")) {
|
|
1892
|
+
// First param is source, second is props
|
|
1893
|
+
props = propsOrUndefined || {};
|
|
1894
|
+
props.source = idOrSourceOrProps;
|
|
1895
|
+
// Generate ID from source
|
|
1896
|
+
const sourceName = idOrSourceOrProps
|
|
1897
|
+
.replace("aws.", "")
|
|
1898
|
+
.split(".")
|
|
1899
|
+
.map((part) => part.charAt(0).toUpperCase() + part.slice(1))
|
|
1900
|
+
.join("");
|
|
1901
|
+
id = props.id || `${sourceName}EventsRule`;
|
|
1902
|
+
}
|
|
1903
|
+
else {
|
|
1904
|
+
// First param is ID, second is props
|
|
1905
|
+
props = propsOrUndefined || {};
|
|
1906
|
+
id = idOrSourceOrProps;
|
|
1907
|
+
}
|
|
1908
|
+
}
|
|
1909
|
+
else {
|
|
1910
|
+
// First param is props
|
|
1911
|
+
props = idOrSourceOrProps || {};
|
|
1912
|
+
if (props.source) {
|
|
1913
|
+
const sourceName = typeof props.source === "string"
|
|
1914
|
+
? props.source
|
|
1915
|
+
.replace("aws.", "")
|
|
1916
|
+
.split(".")
|
|
1917
|
+
.map((part) => part.charAt(0).toUpperCase() + part.slice(1))
|
|
1918
|
+
.join("")
|
|
1919
|
+
: "Events";
|
|
1920
|
+
id = props.id || `${sourceName}EventsRule`;
|
|
1921
|
+
}
|
|
1922
|
+
else {
|
|
1923
|
+
id = props.id || "EventsRule";
|
|
1924
|
+
}
|
|
1925
|
+
}
|
|
1926
|
+
super(scope, id);
|
|
1927
|
+
// Extract Jaypie-specific options
|
|
1928
|
+
const { id: _id, project, service = CDK$2.SERVICE.DATADOG, source, targetFunction, vendor = CDK$2.VENDOR.DATADOG, ...ruleProps } = props;
|
|
1929
|
+
// Resolve target function
|
|
1930
|
+
this.targetFunction =
|
|
1931
|
+
targetFunction || resolveDatadogForwarderFunction(scope);
|
|
1932
|
+
// Build event pattern if source is specified
|
|
1933
|
+
const eventPattern = source
|
|
1934
|
+
? {
|
|
1935
|
+
...ruleProps.eventPattern,
|
|
1936
|
+
source: Array.isArray(source) ? source : [source],
|
|
1937
|
+
}
|
|
1938
|
+
: ruleProps.eventPattern;
|
|
1939
|
+
// Build rule props
|
|
1940
|
+
const finalRuleProps = {
|
|
1941
|
+
...ruleProps,
|
|
1942
|
+
eventPattern,
|
|
1943
|
+
targets: [
|
|
1944
|
+
new awsEventsTargets.LambdaFunction(this.targetFunction, {
|
|
1945
|
+
event: awsEvents.RuleTargetInput.fromEventPath("$"),
|
|
1946
|
+
}),
|
|
1947
|
+
],
|
|
1948
|
+
};
|
|
1949
|
+
// Create the rule
|
|
1950
|
+
this.rule = new awsEvents.Rule(this, "Rule", finalRuleProps);
|
|
1951
|
+
// Add tags
|
|
1952
|
+
cdk__namespace.Tags.of(this.rule).add(CDK$2.TAG.ROLE, CDK$2.ROLE.MONITORING);
|
|
1953
|
+
cdk__namespace.Tags.of(this.rule).add(CDK$2.TAG.SERVICE, service);
|
|
1954
|
+
cdk__namespace.Tags.of(this.rule).add(CDK$2.TAG.VENDOR, vendor);
|
|
1955
|
+
if (project) {
|
|
1956
|
+
cdk__namespace.Tags.of(this.rule).add(CDK$2.TAG.PROJECT, project);
|
|
1957
|
+
}
|
|
1351
1958
|
}
|
|
1352
1959
|
}
|
|
1353
1960
|
|
|
1354
1961
|
class JaypieGitHubDeployRole extends constructs.Construct {
|
|
1355
1962
|
constructor(scope, id = "GitHubDeployRole", props = {}) {
|
|
1356
1963
|
super(scope, id);
|
|
1357
|
-
const { oidcProviderArn = cdk
|
|
1964
|
+
const { oidcProviderArn = cdk.Fn.importValue(CDK$2.IMPORT.OIDC_PROVIDER), output = true, repoRestriction: propsRepoRestriction, } = props;
|
|
1358
1965
|
// Extract account ID from the scope
|
|
1359
|
-
const accountId = cdk
|
|
1966
|
+
const accountId = cdk.Stack.of(this).account;
|
|
1360
1967
|
// Resolve repoRestriction from props or environment variables
|
|
1361
1968
|
let repoRestriction = propsRepoRestriction;
|
|
1362
1969
|
if (!repoRestriction) {
|
|
1363
1970
|
const envRepo = process.env.CDK_ENV_REPO || process.env.PROJECT_REPO;
|
|
1364
1971
|
if (!envRepo) {
|
|
1365
|
-
throw new
|
|
1972
|
+
throw new errors.ConfigurationError("No repoRestriction provided. Set repoRestriction prop, CDK_ENV_REPO, or PROJECT_REPO environment variable");
|
|
1366
1973
|
}
|
|
1367
1974
|
// Extract organization from owner/repo format and create org-wide restriction
|
|
1368
1975
|
const organization = envRepo.split("/")[0];
|
|
@@ -1375,10 +1982,10 @@ class JaypieGitHubDeployRole extends constructs.Construct {
|
|
|
1375
1982
|
"token.actions.githubusercontent.com:sub": repoRestriction,
|
|
1376
1983
|
},
|
|
1377
1984
|
}, "sts:AssumeRoleWithWebIdentity"),
|
|
1378
|
-
maxSessionDuration: cdk
|
|
1985
|
+
maxSessionDuration: cdk.Duration.hours(1),
|
|
1379
1986
|
path: "/",
|
|
1380
1987
|
});
|
|
1381
|
-
cdk
|
|
1988
|
+
cdk.Tags.of(this._role).add(CDK$2.TAG.ROLE, CDK$2.ROLE.DEPLOY);
|
|
1382
1989
|
// Allow the role to access the GitHub OIDC provider
|
|
1383
1990
|
this._role.addToPolicy(new awsIam.PolicyStatement({
|
|
1384
1991
|
actions: ["sts:AssumeRoleWithWebIdentity"],
|
|
@@ -1417,7 +2024,7 @@ class JaypieGitHubDeployRole extends constructs.Construct {
|
|
|
1417
2024
|
// Export the ARN of the role
|
|
1418
2025
|
if (output !== false) {
|
|
1419
2026
|
const outputId = typeof output === "string" ? output : "GitHubActionsRoleArn";
|
|
1420
|
-
new cdk
|
|
2027
|
+
new cdk.CfnOutput(this, outputId, {
|
|
1421
2028
|
value: this._role.roleArn,
|
|
1422
2029
|
});
|
|
1423
2030
|
}
|
|
@@ -1436,8 +2043,8 @@ class JaypieGitHubDeployRole extends constructs.Construct {
|
|
|
1436
2043
|
class JaypieExpressLambda extends JaypieLambda {
|
|
1437
2044
|
constructor(scope, id, props) {
|
|
1438
2045
|
super(scope, id, {
|
|
1439
|
-
timeout: cdk
|
|
1440
|
-
roleTag:
|
|
2046
|
+
timeout: cdk.Duration.seconds(CDK$2.DURATION.EXPRESS_API),
|
|
2047
|
+
roleTag: CDK$2.ROLE.API,
|
|
1441
2048
|
...props,
|
|
1442
2049
|
});
|
|
1443
2050
|
}
|
|
@@ -1497,7 +2104,7 @@ class JaypieHostedZone extends constructs.Construct {
|
|
|
1497
2104
|
super(scope, id);
|
|
1498
2105
|
const { zoneName, project } = props;
|
|
1499
2106
|
const destination = props.destination ?? true;
|
|
1500
|
-
const service = props.service ||
|
|
2107
|
+
const service = props.service || CDK$2.SERVICE.INFRASTRUCTURE;
|
|
1501
2108
|
// Create the log group
|
|
1502
2109
|
this.logGroup = new awsLogs.LogGroup(this, "LogGroup", {
|
|
1503
2110
|
logGroupName: process.env.PROJECT_NONCE
|
|
@@ -1506,10 +2113,10 @@ class JaypieHostedZone extends constructs.Construct {
|
|
|
1506
2113
|
retention: awsLogs.RetentionDays.ONE_WEEK,
|
|
1507
2114
|
});
|
|
1508
2115
|
// Add tags
|
|
1509
|
-
cdk__namespace.Tags.of(this.logGroup).add(
|
|
1510
|
-
cdk__namespace.Tags.of(this.logGroup).add(
|
|
2116
|
+
cdk__namespace.Tags.of(this.logGroup).add(CDK$2.TAG.SERVICE, service);
|
|
2117
|
+
cdk__namespace.Tags.of(this.logGroup).add(CDK$2.TAG.ROLE, CDK$2.ROLE.NETWORKING);
|
|
1511
2118
|
if (project) {
|
|
1512
|
-
cdk__namespace.Tags.of(this.logGroup).add(
|
|
2119
|
+
cdk__namespace.Tags.of(this.logGroup).add(CDK$2.TAG.PROJECT, project);
|
|
1513
2120
|
}
|
|
1514
2121
|
// Grant Route 53 permissions to write to the log group
|
|
1515
2122
|
this.logGroup.grantWrite(new awsIam.ServicePrincipal(SERVICE.ROUTE53));
|
|
@@ -1529,10 +2136,10 @@ class JaypieHostedZone extends constructs.Construct {
|
|
|
1529
2136
|
zoneName,
|
|
1530
2137
|
});
|
|
1531
2138
|
// Add tags
|
|
1532
|
-
cdk__namespace.Tags.of(this.hostedZone).add(
|
|
1533
|
-
cdk__namespace.Tags.of(this.hostedZone).add(
|
|
2139
|
+
cdk__namespace.Tags.of(this.hostedZone).add(CDK$2.TAG.SERVICE, service);
|
|
2140
|
+
cdk__namespace.Tags.of(this.hostedZone).add(CDK$2.TAG.ROLE, CDK$2.ROLE.NETWORKING);
|
|
1534
2141
|
if (project) {
|
|
1535
|
-
cdk__namespace.Tags.of(this.hostedZone).add(
|
|
2142
|
+
cdk__namespace.Tags.of(this.hostedZone).add(CDK$2.TAG.PROJECT, project);
|
|
1536
2143
|
}
|
|
1537
2144
|
// Create DNS records if provided
|
|
1538
2145
|
this.dnsRecords = [];
|
|
@@ -1567,7 +2174,7 @@ class JaypieInfrastructureStack extends JaypieStack {
|
|
|
1567
2174
|
super(scope, id, { key, ...stackProps });
|
|
1568
2175
|
// Add infrastructure-specific tag
|
|
1569
2176
|
if (process.env.CDK_ENV_INFRASTRUCTURE_STACK_SHA) {
|
|
1570
|
-
cdk
|
|
2177
|
+
cdk.Tags.of(this).add(CDK.TAG.STACK_SHA, process.env.CDK_ENV_INFRASTRUCTURE_STACK_SHA);
|
|
1571
2178
|
}
|
|
1572
2179
|
}
|
|
1573
2180
|
}
|
|
@@ -1576,8 +2183,8 @@ class JaypieMongoDbSecret extends JaypieEnvSecret {
|
|
|
1576
2183
|
constructor(scope, id = "MongoConnectionString", props) {
|
|
1577
2184
|
const defaultProps = {
|
|
1578
2185
|
envKey: "MONGODB_URI",
|
|
1579
|
-
roleTag:
|
|
1580
|
-
vendorTag:
|
|
2186
|
+
roleTag: CDK$2.ROLE.STORAGE,
|
|
2187
|
+
vendorTag: CDK$2.VENDOR.MONGODB,
|
|
1581
2188
|
...props,
|
|
1582
2189
|
};
|
|
1583
2190
|
super(scope, id, defaultProps);
|
|
@@ -1588,14 +2195,108 @@ class JaypieOpenAiSecret extends JaypieEnvSecret {
|
|
|
1588
2195
|
constructor(scope, id = "OpenAiApiKey", props) {
|
|
1589
2196
|
const defaultProps = {
|
|
1590
2197
|
envKey: "OPENAI_API_KEY",
|
|
1591
|
-
roleTag:
|
|
1592
|
-
vendorTag:
|
|
2198
|
+
roleTag: CDK$2.ROLE.PROCESSING,
|
|
2199
|
+
vendorTag: CDK$2.VENDOR.OPENAI,
|
|
1593
2200
|
...props,
|
|
1594
2201
|
};
|
|
1595
2202
|
super(scope, id, defaultProps);
|
|
1596
2203
|
}
|
|
1597
2204
|
}
|
|
1598
2205
|
|
|
2206
|
+
class JaypieOrganizationTrail extends constructs.Construct {
|
|
2207
|
+
/**
|
|
2208
|
+
* Create a new organization CloudTrail with S3 bucket and lifecycle policies
|
|
2209
|
+
*/
|
|
2210
|
+
constructor(scope, idOrProps, propsOrUndefined) {
|
|
2211
|
+
// Handle overloaded constructor signatures
|
|
2212
|
+
let props;
|
|
2213
|
+
let id;
|
|
2214
|
+
if (typeof idOrProps === "string") {
|
|
2215
|
+
// First param is ID, second is props
|
|
2216
|
+
props = propsOrUndefined || {};
|
|
2217
|
+
id = idOrProps;
|
|
2218
|
+
}
|
|
2219
|
+
else {
|
|
2220
|
+
// First param is props
|
|
2221
|
+
props = idOrProps || {};
|
|
2222
|
+
const defaultName = process.env.PROJECT_NONCE
|
|
2223
|
+
? `organization-cloudtrail-${process.env.PROJECT_NONCE}`
|
|
2224
|
+
: "organization-cloudtrail";
|
|
2225
|
+
id = props.id || `${props.trailName || defaultName}-Trail`;
|
|
2226
|
+
}
|
|
2227
|
+
super(scope, id);
|
|
2228
|
+
// Resolve options with defaults
|
|
2229
|
+
const { bucketName = process.env.PROJECT_NONCE
|
|
2230
|
+
? `organization-cloudtrail-${process.env.PROJECT_NONCE}`
|
|
2231
|
+
: "organization-cloudtrail", enableDatadogNotifications = true, enableFileValidation = false, expirationDays = 365, glacierTransitionDays = 180, infrequentAccessTransitionDays = 30, project, service = CDK$2.SERVICE.INFRASTRUCTURE, trailName = process.env.PROJECT_NONCE
|
|
2232
|
+
? `organization-cloudtrail-${process.env.PROJECT_NONCE}`
|
|
2233
|
+
: "organization-cloudtrail", } = props;
|
|
2234
|
+
// Create the S3 bucket for CloudTrail logs
|
|
2235
|
+
this.bucket = new s3.Bucket(this, "Bucket", {
|
|
2236
|
+
accessControl: s3.BucketAccessControl.LOG_DELIVERY_WRITE,
|
|
2237
|
+
bucketName,
|
|
2238
|
+
lifecycleRules: [
|
|
2239
|
+
{
|
|
2240
|
+
expiration: cdk__namespace.Duration.days(expirationDays),
|
|
2241
|
+
transitions: [
|
|
2242
|
+
{
|
|
2243
|
+
storageClass: s3.StorageClass.INFREQUENT_ACCESS,
|
|
2244
|
+
transitionAfter: cdk__namespace.Duration.days(infrequentAccessTransitionDays),
|
|
2245
|
+
},
|
|
2246
|
+
{
|
|
2247
|
+
storageClass: s3.StorageClass.GLACIER,
|
|
2248
|
+
transitionAfter: cdk__namespace.Duration.days(glacierTransitionDays),
|
|
2249
|
+
},
|
|
2250
|
+
],
|
|
2251
|
+
},
|
|
2252
|
+
],
|
|
2253
|
+
});
|
|
2254
|
+
// Add CloudTrail bucket policies
|
|
2255
|
+
this.bucket.addToResourcePolicy(new awsIam.PolicyStatement({
|
|
2256
|
+
actions: ["s3:GetBucketAcl"],
|
|
2257
|
+
effect: awsIam.Effect.ALLOW,
|
|
2258
|
+
principals: [new awsIam.ServicePrincipal("cloudtrail.amazonaws.com")],
|
|
2259
|
+
resources: [this.bucket.bucketArn],
|
|
2260
|
+
}));
|
|
2261
|
+
this.bucket.addToResourcePolicy(new awsIam.PolicyStatement({
|
|
2262
|
+
actions: ["s3:PutObject"],
|
|
2263
|
+
conditions: {
|
|
2264
|
+
StringEquals: {
|
|
2265
|
+
"s3:x-amz-acl": "bucket-owner-full-control",
|
|
2266
|
+
},
|
|
2267
|
+
},
|
|
2268
|
+
effect: awsIam.Effect.ALLOW,
|
|
2269
|
+
principals: [new awsIam.ServicePrincipal("cloudtrail.amazonaws.com")],
|
|
2270
|
+
resources: [`${this.bucket.bucketArn}/*`],
|
|
2271
|
+
}));
|
|
2272
|
+
// Add tags to bucket
|
|
2273
|
+
cdk__namespace.Tags.of(this.bucket).add(CDK$2.TAG.SERVICE, service);
|
|
2274
|
+
cdk__namespace.Tags.of(this.bucket).add(CDK$2.TAG.ROLE, CDK$2.ROLE.MONITORING);
|
|
2275
|
+
if (project) {
|
|
2276
|
+
cdk__namespace.Tags.of(this.bucket).add(CDK$2.TAG.PROJECT, project);
|
|
2277
|
+
}
|
|
2278
|
+
// Add Datadog notifications if enabled
|
|
2279
|
+
if (enableDatadogNotifications) {
|
|
2280
|
+
const datadogForwarderFunction = resolveDatadogForwarderFunction(scope);
|
|
2281
|
+
this.bucket.addEventNotification(s3.EventType.OBJECT_CREATED, new s3n.LambdaDestination(datadogForwarderFunction));
|
|
2282
|
+
}
|
|
2283
|
+
// Create the organization trail
|
|
2284
|
+
this.trail = new awsCloudtrail.Trail(this, "Trail", {
|
|
2285
|
+
bucket: this.bucket,
|
|
2286
|
+
enableFileValidation,
|
|
2287
|
+
isOrganizationTrail: true,
|
|
2288
|
+
managementEvents: awsCloudtrail.ReadWriteType.ALL,
|
|
2289
|
+
trailName,
|
|
2290
|
+
});
|
|
2291
|
+
// Add tags to trail
|
|
2292
|
+
cdk__namespace.Tags.of(this.trail).add(CDK$2.TAG.SERVICE, service);
|
|
2293
|
+
cdk__namespace.Tags.of(this.trail).add(CDK$2.TAG.ROLE, CDK$2.ROLE.MONITORING);
|
|
2294
|
+
if (project) {
|
|
2295
|
+
cdk__namespace.Tags.of(this.trail).add(CDK$2.TAG.PROJECT, project);
|
|
2296
|
+
}
|
|
2297
|
+
}
|
|
2298
|
+
}
|
|
2299
|
+
|
|
1599
2300
|
/**
|
|
1600
2301
|
* JaypieSsoPermissions Construct
|
|
1601
2302
|
*
|
|
@@ -1662,15 +2363,15 @@ class JaypieSsoPermissions extends constructs.Construct {
|
|
|
1662
2363
|
.managedPolicyArn,
|
|
1663
2364
|
awsIam.ManagedPolicy.fromAwsManagedPolicyName("AWSManagementConsoleBasicUserAccess").managedPolicyArn,
|
|
1664
2365
|
],
|
|
1665
|
-
sessionDuration: cdk
|
|
2366
|
+
sessionDuration: cdk.Duration.hours(1).toIsoString(),
|
|
1666
2367
|
tags: [
|
|
1667
2368
|
{
|
|
1668
|
-
key:
|
|
1669
|
-
value:
|
|
2369
|
+
key: CDK$2.TAG.SERVICE,
|
|
2370
|
+
value: CDK$2.SERVICE.SSO,
|
|
1670
2371
|
},
|
|
1671
2372
|
{
|
|
1672
|
-
key:
|
|
1673
|
-
value:
|
|
2373
|
+
key: CDK$2.TAG.ROLE,
|
|
2374
|
+
value: CDK$2.ROLE.SECURITY,
|
|
1674
2375
|
},
|
|
1675
2376
|
],
|
|
1676
2377
|
});
|
|
@@ -1744,15 +2445,15 @@ class JaypieSsoPermissions extends constructs.Construct {
|
|
|
1744
2445
|
awsIam.ManagedPolicy.fromAwsManagedPolicyName("ReadOnlyAccess")
|
|
1745
2446
|
.managedPolicyArn,
|
|
1746
2447
|
],
|
|
1747
|
-
sessionDuration: cdk
|
|
2448
|
+
sessionDuration: cdk.Duration.hours(12).toIsoString(),
|
|
1748
2449
|
tags: [
|
|
1749
2450
|
{
|
|
1750
|
-
key:
|
|
1751
|
-
value:
|
|
2451
|
+
key: CDK$2.TAG.SERVICE,
|
|
2452
|
+
value: CDK$2.SERVICE.SSO,
|
|
1752
2453
|
},
|
|
1753
2454
|
{
|
|
1754
|
-
key:
|
|
1755
|
-
value:
|
|
2455
|
+
key: CDK$2.TAG.ROLE,
|
|
2456
|
+
value: CDK$2.ROLE.SECURITY,
|
|
1756
2457
|
},
|
|
1757
2458
|
],
|
|
1758
2459
|
});
|
|
@@ -1802,15 +2503,15 @@ class JaypieSsoPermissions extends constructs.Construct {
|
|
|
1802
2503
|
.managedPolicyArn,
|
|
1803
2504
|
awsIam.ManagedPolicy.fromAwsManagedPolicyName("job-function/SystemAdministrator").managedPolicyArn,
|
|
1804
2505
|
],
|
|
1805
|
-
sessionDuration: cdk
|
|
2506
|
+
sessionDuration: cdk.Duration.hours(4).toIsoString(),
|
|
1806
2507
|
tags: [
|
|
1807
2508
|
{
|
|
1808
|
-
key:
|
|
1809
|
-
value:
|
|
2509
|
+
key: CDK$2.TAG.SERVICE,
|
|
2510
|
+
value: CDK$2.SERVICE.SSO,
|
|
1810
2511
|
},
|
|
1811
2512
|
{
|
|
1812
|
-
key:
|
|
1813
|
-
value:
|
|
2513
|
+
key: CDK$2.TAG.ROLE,
|
|
2514
|
+
value: CDK$2.ROLE.SECURITY,
|
|
1814
2515
|
},
|
|
1815
2516
|
],
|
|
1816
2517
|
});
|
|
@@ -1842,19 +2543,19 @@ class JaypieSsoPermissions extends constructs.Construct {
|
|
|
1842
2543
|
permissionSetNames.forEach((permissionSetName) => {
|
|
1843
2544
|
const permissionSet = permissionSetMap[permissionSetName];
|
|
1844
2545
|
if (!permissionSet) {
|
|
1845
|
-
throw new
|
|
2546
|
+
throw new errors.ConfigurationError(`Unknown permission set: ${permissionSetName}. Valid options: ${Object.keys(permissionSetMap).join(", ")}`);
|
|
1846
2547
|
}
|
|
1847
2548
|
const accountAssignment = new awsSso.CfnAssignment(this, `AccountAssignment-${accountId}-${permissionSet.label}Role-${groupId}Group`, {
|
|
1848
2549
|
// Required
|
|
1849
2550
|
instanceArn: iamIdentityCenterArn,
|
|
1850
2551
|
permissionSetArn: permissionSet.arn,
|
|
1851
2552
|
principalId: groupId,
|
|
1852
|
-
principalType:
|
|
2553
|
+
principalType: CDK$2.PRINCIPAL_TYPE.GROUP,
|
|
1853
2554
|
targetId: accountId,
|
|
1854
|
-
targetType:
|
|
2555
|
+
targetType: CDK$2.TARGET_TYPE.AWS_ACCOUNT,
|
|
1855
2556
|
});
|
|
1856
|
-
cdk
|
|
1857
|
-
cdk
|
|
2557
|
+
cdk.Tags.of(accountAssignment).add(CDK$2.TAG.SERVICE, CDK$2.SERVICE.SSO);
|
|
2558
|
+
cdk.Tags.of(accountAssignment).add(CDK$2.TAG.ROLE, CDK$2.ROLE.SECURITY);
|
|
1858
2559
|
});
|
|
1859
2560
|
});
|
|
1860
2561
|
};
|
|
@@ -1924,18 +2625,18 @@ class JaypieSsoSyncApplication extends constructs.Construct {
|
|
|
1924
2625
|
GoogleCredentials: resolvedGoogleCredentials,
|
|
1925
2626
|
GoogleGroupMatch: resolvedGoogleGroupMatch,
|
|
1926
2627
|
IdentityStoreID: resolvedIdentityStoreId,
|
|
1927
|
-
Region: cdk
|
|
2628
|
+
Region: cdk.Stack.of(this).region,
|
|
1928
2629
|
SCIMEndpointAccessToken: resolvedScimEndpointAccessToken,
|
|
1929
2630
|
SCIMEndpointUrl: resolvedScimEndpointUrl,
|
|
1930
2631
|
},
|
|
1931
2632
|
});
|
|
1932
2633
|
// Add tags
|
|
1933
2634
|
const defaultTags = {
|
|
1934
|
-
[
|
|
2635
|
+
[CDK$2.TAG.ROLE]: CDK$2.ROLE.SECURITY,
|
|
1935
2636
|
};
|
|
1936
2637
|
const allTags = { ...defaultTags, ...tags };
|
|
1937
2638
|
Object.entries(allTags).forEach(([key, value]) => {
|
|
1938
|
-
cdk
|
|
2639
|
+
cdk.Tags.of(this._application).add(key, value);
|
|
1939
2640
|
});
|
|
1940
2641
|
}
|
|
1941
2642
|
get application() {
|
|
@@ -1947,8 +2648,8 @@ class JaypieTraceSigningKeySecret extends JaypieEnvSecret {
|
|
|
1947
2648
|
constructor(scope, id = "TraceSigningKey", props) {
|
|
1948
2649
|
const defaultProps = {
|
|
1949
2650
|
envKey: "TRACE_SIGNING_KEY",
|
|
1950
|
-
roleTag:
|
|
1951
|
-
vendorTag:
|
|
2651
|
+
roleTag: CDK$2.ROLE.API,
|
|
2652
|
+
vendorTag: CDK$2.VENDOR.KNOWTRACE,
|
|
1952
2653
|
...props,
|
|
1953
2654
|
};
|
|
1954
2655
|
super(scope, id, defaultProps);
|
|
@@ -1958,19 +2659,19 @@ class JaypieTraceSigningKeySecret extends JaypieEnvSecret {
|
|
|
1958
2659
|
class JaypieWebDeploymentBucket extends constructs.Construct {
|
|
1959
2660
|
constructor(scope, id, props = {}) {
|
|
1960
2661
|
super(scope, id);
|
|
1961
|
-
const roleTag = props.roleTag ||
|
|
2662
|
+
const roleTag = props.roleTag || CDK$2.ROLE.HOSTING;
|
|
1962
2663
|
// Environment variable validation
|
|
1963
2664
|
if (process.env.CDK_ENV_WEB_SUBDOMAIN &&
|
|
1964
|
-
!
|
|
1965
|
-
throw new
|
|
2665
|
+
!isValidSubdomain(process.env.CDK_ENV_WEB_SUBDOMAIN)) {
|
|
2666
|
+
throw new errors.ConfigurationError("CDK_ENV_WEB_SUBDOMAIN is not a valid subdomain");
|
|
1966
2667
|
}
|
|
1967
2668
|
if (process.env.CDK_ENV_WEB_HOSTED_ZONE &&
|
|
1968
|
-
!
|
|
1969
|
-
throw new
|
|
2669
|
+
!isValidHostname$1(process.env.CDK_ENV_WEB_HOSTED_ZONE)) {
|
|
2670
|
+
throw new errors.ConfigurationError("CDK_ENV_WEB_HOSTED_ZONE is not a valid hostname");
|
|
1970
2671
|
}
|
|
1971
2672
|
if (process.env.CDK_ENV_HOSTED_ZONE &&
|
|
1972
|
-
!
|
|
1973
|
-
throw new
|
|
2673
|
+
!isValidHostname$1(process.env.CDK_ENV_HOSTED_ZONE)) {
|
|
2674
|
+
throw new errors.ConfigurationError("CDK_ENV_HOSTED_ZONE is not a valid hostname");
|
|
1974
2675
|
}
|
|
1975
2676
|
// Determine host from props or environment
|
|
1976
2677
|
let host = props.host;
|
|
@@ -1978,7 +2679,7 @@ class JaypieWebDeploymentBucket extends constructs.Construct {
|
|
|
1978
2679
|
try {
|
|
1979
2680
|
host =
|
|
1980
2681
|
process.env.CDK_ENV_WEB_HOST ||
|
|
1981
|
-
|
|
2682
|
+
mergeDomain(process.env.CDK_ENV_WEB_SUBDOMAIN || "", process.env.CDK_ENV_WEB_HOSTED_ZONE ||
|
|
1982
2683
|
process.env.CDK_ENV_HOSTED_ZONE ||
|
|
1983
2684
|
"");
|
|
1984
2685
|
}
|
|
@@ -1986,8 +2687,8 @@ class JaypieWebDeploymentBucket extends constructs.Construct {
|
|
|
1986
2687
|
host = undefined;
|
|
1987
2688
|
}
|
|
1988
2689
|
}
|
|
1989
|
-
if (host && !
|
|
1990
|
-
throw new
|
|
2690
|
+
if (host && !isValidHostname$1(host)) {
|
|
2691
|
+
throw new errors.ConfigurationError("Host is not a valid hostname");
|
|
1991
2692
|
}
|
|
1992
2693
|
// Determine zone from props or environment
|
|
1993
2694
|
const zone = props.zone ||
|
|
@@ -2000,7 +2701,7 @@ class JaypieWebDeploymentBucket extends constructs.Construct {
|
|
|
2000
2701
|
blockPublicAccess: s3__namespace.BlockPublicAccess.BLOCK_ACLS,
|
|
2001
2702
|
bucketName: props.name || constructEnvName("web"),
|
|
2002
2703
|
publicReadAccess: true,
|
|
2003
|
-
removalPolicy: cdk
|
|
2704
|
+
removalPolicy: cdk.RemovalPolicy.DESTROY,
|
|
2004
2705
|
versioned: false,
|
|
2005
2706
|
websiteErrorDocument: "index.html",
|
|
2006
2707
|
websiteIndexDocument: "index.html",
|
|
@@ -2018,7 +2719,7 @@ class JaypieWebDeploymentBucket extends constructs.Construct {
|
|
|
2018
2719
|
this.isWebsite = this.bucket.isWebsite;
|
|
2019
2720
|
this.notificationsHandlerRole = undefined;
|
|
2020
2721
|
this.policy = this.bucket.policy;
|
|
2021
|
-
cdk
|
|
2722
|
+
cdk.Tags.of(this.bucket).add(CDK$2.TAG.ROLE, roleTag);
|
|
2022
2723
|
// Create deployment role if repository is configured
|
|
2023
2724
|
let repo;
|
|
2024
2725
|
if (process.env.CDK_ENV_REPO) {
|
|
@@ -2026,14 +2727,14 @@ class JaypieWebDeploymentBucket extends constructs.Construct {
|
|
|
2026
2727
|
}
|
|
2027
2728
|
if (repo) {
|
|
2028
2729
|
const bucketDeployRole = new awsIam.Role(this, "DestinationBucketDeployRole", {
|
|
2029
|
-
assumedBy: new awsIam.FederatedPrincipal(cdk
|
|
2730
|
+
assumedBy: new awsIam.FederatedPrincipal(cdk.Fn.importValue(CDK$2.IMPORT.OIDC_PROVIDER), {
|
|
2030
2731
|
StringLike: {
|
|
2031
2732
|
"token.actions.githubusercontent.com:sub": repo,
|
|
2032
2733
|
},
|
|
2033
2734
|
}, "sts:AssumeRoleWithWebIdentity"),
|
|
2034
|
-
maxSessionDuration: cdk
|
|
2735
|
+
maxSessionDuration: cdk.Duration.hours(1),
|
|
2035
2736
|
});
|
|
2036
|
-
cdk
|
|
2737
|
+
cdk.Tags.of(bucketDeployRole).add(CDK$2.TAG.ROLE, CDK$2.ROLE.DEPLOY);
|
|
2037
2738
|
// Allow the role to write to the bucket
|
|
2038
2739
|
bucketDeployRole.addToPolicy(new awsIam.PolicyStatement({
|
|
2039
2740
|
effect: awsIam.Effect.ALLOW,
|
|
@@ -2058,7 +2759,7 @@ class JaypieWebDeploymentBucket extends constructs.Construct {
|
|
|
2058
2759
|
}));
|
|
2059
2760
|
this.deployRoleArn = bucketDeployRole.roleArn;
|
|
2060
2761
|
// Output the deploy role ARN
|
|
2061
|
-
new cdk
|
|
2762
|
+
new cdk.CfnOutput(this, "DestinationBucketDeployRoleArn", {
|
|
2062
2763
|
value: bucketDeployRole.roleArn,
|
|
2063
2764
|
});
|
|
2064
2765
|
}
|
|
@@ -2078,10 +2779,10 @@ class JaypieWebDeploymentBucket extends constructs.Construct {
|
|
|
2078
2779
|
domainName: host,
|
|
2079
2780
|
validation: acm__namespace.CertificateValidation.fromDns(hostedZone),
|
|
2080
2781
|
});
|
|
2081
|
-
new cdk
|
|
2782
|
+
new cdk.CfnOutput(this, "CertificateArn", {
|
|
2082
2783
|
value: this.certificate.certificateArn,
|
|
2083
2784
|
});
|
|
2084
|
-
cdk
|
|
2785
|
+
cdk.Tags.of(this.certificate).add(CDK$2.TAG.ROLE, roleTag);
|
|
2085
2786
|
}
|
|
2086
2787
|
// Create CloudFront distribution
|
|
2087
2788
|
this.distribution = new cloudfront__namespace.Distribution(this, "Distribution", {
|
|
@@ -2093,7 +2794,7 @@ class JaypieWebDeploymentBucket extends constructs.Construct {
|
|
|
2093
2794
|
certificate: this.certificate,
|
|
2094
2795
|
domainNames: [host],
|
|
2095
2796
|
});
|
|
2096
|
-
cdk
|
|
2797
|
+
cdk.Tags.of(this.distribution).add(CDK$2.TAG.ROLE, roleTag);
|
|
2097
2798
|
// If this is production, enable caching on everything but index.html
|
|
2098
2799
|
if (isProductionEnv()) {
|
|
2099
2800
|
this.distribution.addBehavior("/*", new origins__namespace.S3Origin(this.bucket), {
|
|
@@ -2107,7 +2808,7 @@ class JaypieWebDeploymentBucket extends constructs.Construct {
|
|
|
2107
2808
|
target: route53__namespace.RecordTarget.fromAlias(new route53Targets__namespace.CloudFrontTarget(this.distribution)),
|
|
2108
2809
|
zone: hostedZone,
|
|
2109
2810
|
});
|
|
2110
|
-
cdk
|
|
2811
|
+
cdk.Tags.of(record).add(CDK$2.TAG.ROLE, CDK$2.ROLE.NETWORKING);
|
|
2111
2812
|
this.distributionDomainName = this.distribution.distributionDomainName;
|
|
2112
2813
|
}
|
|
2113
2814
|
}
|
|
@@ -2209,16 +2910,17 @@ class JaypieWebDeploymentBucket extends constructs.Construct {
|
|
|
2209
2910
|
}
|
|
2210
2911
|
}
|
|
2211
2912
|
|
|
2212
|
-
|
|
2213
|
-
|
|
2214
|
-
get: function () { return cdk.CDK; }
|
|
2215
|
-
});
|
|
2913
|
+
exports.CDK = CDK$2;
|
|
2914
|
+
exports.JaypieAccountLoggingBucket = JaypieAccountLoggingBucket;
|
|
2216
2915
|
exports.JaypieApiGateway = JaypieApiGateway;
|
|
2217
2916
|
exports.JaypieAppStack = JaypieAppStack;
|
|
2218
2917
|
exports.JaypieBucketQueuedLambda = JaypieBucketQueuedLambda;
|
|
2918
|
+
exports.JaypieDatadogBucket = JaypieDatadogBucket;
|
|
2919
|
+
exports.JaypieDatadogForwarder = JaypieDatadogForwarder;
|
|
2219
2920
|
exports.JaypieDatadogSecret = JaypieDatadogSecret;
|
|
2220
2921
|
exports.JaypieDnsRecord = JaypieDnsRecord;
|
|
2221
2922
|
exports.JaypieEnvSecret = JaypieEnvSecret;
|
|
2923
|
+
exports.JaypieEventsRule = JaypieEventsRule;
|
|
2222
2924
|
exports.JaypieExpressLambda = JaypieExpressLambda;
|
|
2223
2925
|
exports.JaypieGitHubDeployRole = JaypieGitHubDeployRole;
|
|
2224
2926
|
exports.JaypieHostedZone = JaypieHostedZone;
|
|
@@ -2226,6 +2928,7 @@ exports.JaypieInfrastructureStack = JaypieInfrastructureStack;
|
|
|
2226
2928
|
exports.JaypieLambda = JaypieLambda;
|
|
2227
2929
|
exports.JaypieMongoDbSecret = JaypieMongoDbSecret;
|
|
2228
2930
|
exports.JaypieOpenAiSecret = JaypieOpenAiSecret;
|
|
2931
|
+
exports.JaypieOrganizationTrail = JaypieOrganizationTrail;
|
|
2229
2932
|
exports.JaypieQueuedLambda = JaypieQueuedLambda;
|
|
2230
2933
|
exports.JaypieSsoPermissions = JaypieSsoPermissions;
|
|
2231
2934
|
exports.JaypieSsoSyncApplication = JaypieSsoSyncApplication;
|
|
@@ -2237,10 +2940,14 @@ exports.constructEnvName = constructEnvName;
|
|
|
2237
2940
|
exports.constructStackName = constructStackName;
|
|
2238
2941
|
exports.constructTagger = constructTagger;
|
|
2239
2942
|
exports.envHostname = envHostname;
|
|
2943
|
+
exports.extendDatadogRole = extendDatadogRole;
|
|
2240
2944
|
exports.isEnv = isEnv;
|
|
2241
2945
|
exports.isProductionEnv = isProductionEnv;
|
|
2242
2946
|
exports.isSandboxEnv = isSandboxEnv;
|
|
2947
|
+
exports.isValidHostname = isValidHostname$1;
|
|
2948
|
+
exports.isValidSubdomain = isValidSubdomain;
|
|
2243
2949
|
exports.jaypieLambdaEnv = jaypieLambdaEnv;
|
|
2950
|
+
exports.mergeDomain = mergeDomain;
|
|
2244
2951
|
exports.resolveDatadogForwarderFunction = resolveDatadogForwarderFunction;
|
|
2245
2952
|
exports.resolveDatadogLayers = resolveDatadogLayers;
|
|
2246
2953
|
exports.resolveDatadogLoggingDestination = resolveDatadogLoggingDestination;
|