@jaypie/constructs 1.1.53 → 1.1.55
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 +865 -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 +710 -7
- package/dist/esm/index.js.map +1 -1
- package/package.json +3 -3
package/dist/cjs/index.cjs
CHANGED
|
@@ -1,22 +1,25 @@
|
|
|
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
25
|
var cloudfront = require('aws-cdk-lib/aws-cloudfront');
|
|
@@ -39,7 +42,8 @@ function _interopNamespaceDefault(e) {
|
|
|
39
42
|
return Object.freeze(n);
|
|
40
43
|
}
|
|
41
44
|
|
|
42
|
-
var cdk__namespace = /*#__PURE__*/_interopNamespaceDefault(cdk
|
|
45
|
+
var cdk__namespace = /*#__PURE__*/_interopNamespaceDefault(cdk);
|
|
46
|
+
var s3__namespace = /*#__PURE__*/_interopNamespaceDefault(s3);
|
|
43
47
|
var acm__namespace = /*#__PURE__*/_interopNamespaceDefault(acm);
|
|
44
48
|
var apiGateway__namespace = /*#__PURE__*/_interopNamespaceDefault(apiGateway);
|
|
45
49
|
var route53__namespace = /*#__PURE__*/_interopNamespaceDefault(route53);
|
|
@@ -47,13 +51,231 @@ var route53Targets__namespace = /*#__PURE__*/_interopNamespaceDefault(route53Tar
|
|
|
47
51
|
var secretsmanager__namespace = /*#__PURE__*/_interopNamespaceDefault(secretsmanager);
|
|
48
52
|
var lambda__namespace = /*#__PURE__*/_interopNamespaceDefault(lambda);
|
|
49
53
|
var logDestinations__namespace = /*#__PURE__*/_interopNamespaceDefault(logDestinations);
|
|
50
|
-
var s3__namespace = /*#__PURE__*/_interopNamespaceDefault(s3);
|
|
51
54
|
var s3n__namespace = /*#__PURE__*/_interopNamespaceDefault(s3n);
|
|
52
55
|
var sqs__namespace = /*#__PURE__*/_interopNamespaceDefault(sqs);
|
|
53
56
|
var lambdaEventSources__namespace = /*#__PURE__*/_interopNamespaceDefault(lambdaEventSources);
|
|
54
57
|
var cloudfront__namespace = /*#__PURE__*/_interopNamespaceDefault(cloudfront);
|
|
55
58
|
var origins__namespace = /*#__PURE__*/_interopNamespaceDefault(origins);
|
|
56
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
|
+
|
|
57
279
|
function addDatadogLayers(lambdaFunction, options = {}) {
|
|
58
280
|
const datadogApiKeyArn = options?.datadogApiKeyArn;
|
|
59
281
|
const resolvedDatadogApiKeyArn = datadogApiKeyArn ||
|
|
@@ -70,8 +292,8 @@ function addDatadogLayers(lambdaFunction, options = {}) {
|
|
|
70
292
|
DD_PROFILING_ENABLED: "false",
|
|
71
293
|
DD_SERVERLESS_APPSEC_ENABLED: "false",
|
|
72
294
|
DD_SERVICE: process.env.PROJECT_SERVICE || "",
|
|
73
|
-
DD_SITE:
|
|
74
|
-
DD_TAGS: `${
|
|
295
|
+
DD_SITE: CDK$2.DATADOG.SITE,
|
|
296
|
+
DD_TAGS: `${CDK$2.TAG.SPONSOR}:${process.env.PROJECT_SPONSOR || ""}`,
|
|
75
297
|
DD_TRACE_OTEL_ENABLED: "false",
|
|
76
298
|
};
|
|
77
299
|
// Add environment variables only if they don't already exist
|
|
@@ -81,8 +303,8 @@ function addDatadogLayers(lambdaFunction, options = {}) {
|
|
|
81
303
|
const datadogApiKeySecret = secretsmanager__namespace.Secret.fromSecretCompleteArn(lambdaFunction, "DatadogApiKey", resolvedDatadogApiKeyArn);
|
|
82
304
|
const datadogLambda = new datadogCdkConstructsV2.DatadogLambda(lambdaFunction, "DatadogLambda", {
|
|
83
305
|
apiKeySecret: datadogApiKeySecret, // apiKeySecret auto-grants secret access to the added lambdas
|
|
84
|
-
nodeLayerVersion:
|
|
85
|
-
extensionLayerVersion:
|
|
306
|
+
nodeLayerVersion: CDK$2.DATADOG.LAYER.NODE,
|
|
307
|
+
extensionLayerVersion: CDK$2.DATADOG.LAYER.EXTENSION,
|
|
86
308
|
env: process.env.PROJECT_ENV,
|
|
87
309
|
service: process.env.PROJECT_SERVICE,
|
|
88
310
|
version: process.env.PROJECT_VERSION,
|
|
@@ -134,35 +356,35 @@ function constructTagger(construct, { name } = {}) {
|
|
|
134
356
|
const stackName = name || constructStackName();
|
|
135
357
|
const version = process.env.npm_package_version || process.env.PROJECT_VERSION || null;
|
|
136
358
|
if (process.env.PROJECT_COMMIT && process.env.PROJECT_COMMIT.length > 8) {
|
|
137
|
-
cdk
|
|
359
|
+
cdk.Tags.of(construct).add(CDK$1.TAG.BUILD_HEX, process.env.PROJECT_COMMIT.slice(0, 8));
|
|
138
360
|
}
|
|
139
|
-
cdk
|
|
140
|
-
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());
|
|
141
363
|
if (process.env.PROJECT_COMMIT)
|
|
142
|
-
cdk
|
|
143
|
-
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);
|
|
144
366
|
if (process.env.PROJECT_ENV)
|
|
145
|
-
cdk
|
|
367
|
+
cdk.Tags.of(construct).add(CDK$1.TAG.ENV, process.env.PROJECT_ENV);
|
|
146
368
|
if (process.env.PROJECT_NONCE)
|
|
147
|
-
cdk
|
|
369
|
+
cdk.Tags.of(construct).add(CDK$1.TAG.NONCE, process.env.PROJECT_NONCE);
|
|
148
370
|
if (process.env.PROJECT_KEY)
|
|
149
|
-
cdk
|
|
150
|
-
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);
|
|
151
373
|
if (process.env.PROJECT_SERVICE)
|
|
152
|
-
cdk
|
|
374
|
+
cdk.Tags.of(construct).add(CDK$1.TAG.SERVICE, process.env.PROJECT_SERVICE);
|
|
153
375
|
if (process.env.PROJECT_SPONSOR)
|
|
154
|
-
cdk
|
|
376
|
+
cdk.Tags.of(construct).add(CDK$1.TAG.SPONSOR, process.env.PROJECT_SPONSOR);
|
|
155
377
|
if (stackName)
|
|
156
|
-
cdk
|
|
378
|
+
cdk.Tags.of(construct).add(CDK$1.TAG.STACK, stackName);
|
|
157
379
|
if (version)
|
|
158
|
-
cdk
|
|
380
|
+
cdk.Tags.of(construct).add(CDK$1.TAG.VERSION, version);
|
|
159
381
|
return true;
|
|
160
382
|
}
|
|
161
383
|
|
|
162
384
|
function envHostname({ component, domain, env, subdomain, } = {}) {
|
|
163
385
|
const resolvedDomain = domain || process.env.CDK_ENV_DOMAIN || process.env.CDK_ENV_HOSTED_ZONE;
|
|
164
386
|
if (!resolvedDomain) {
|
|
165
|
-
throw new
|
|
387
|
+
throw new errors.ConfigurationError("No hostname `domain` provided. Set CDK_ENV_DOMAIN or CDK_ENV_HOSTED_ZONE to use environment domain");
|
|
166
388
|
}
|
|
167
389
|
const resolvedComponent = component === "@" || component === "" ? undefined : component;
|
|
168
390
|
const resolvedSubdomain = subdomain || process.env.CDK_ENV_SUBDOMAIN;
|
|
@@ -176,6 +398,55 @@ function envHostname({ component, domain, env, subdomain, } = {}) {
|
|
|
176
398
|
return parts.join(".");
|
|
177
399
|
}
|
|
178
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
|
+
|
|
179
450
|
/**
|
|
180
451
|
* Check if the current environment matches the given environment
|
|
181
452
|
*/
|
|
@@ -186,13 +457,86 @@ function isEnv(env) {
|
|
|
186
457
|
* Check if the current environment is production
|
|
187
458
|
*/
|
|
188
459
|
function isProductionEnv() {
|
|
189
|
-
return isEnv(
|
|
460
|
+
return isEnv(CDK$2.ENV.PRODUCTION);
|
|
190
461
|
}
|
|
191
462
|
/**
|
|
192
463
|
* Check if the current environment is sandbox
|
|
193
464
|
*/
|
|
194
465
|
function isSandboxEnv() {
|
|
195
|
-
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;
|
|
196
540
|
}
|
|
197
541
|
|
|
198
542
|
function jaypieLambdaEnv(options = {}) {
|
|
@@ -249,6 +593,21 @@ function jaypieLambdaEnv(options = {}) {
|
|
|
249
593
|
return environment;
|
|
250
594
|
}
|
|
251
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
|
+
|
|
252
611
|
const DEFAULT_FUNCTION_NAME$1 = "DatadogForwarderFunction";
|
|
253
612
|
// Cache to store resolved functions
|
|
254
613
|
// Using nested structure to support multiple functions per scope with automatic GC
|
|
@@ -256,7 +615,7 @@ const functionCache = new WeakMap();
|
|
|
256
615
|
function resolveDatadogForwarderFunction(scope, options) {
|
|
257
616
|
const { import: importValue, name } = options || {};
|
|
258
617
|
const functionName = name || DEFAULT_FUNCTION_NAME$1;
|
|
259
|
-
const importKey = importValue ||
|
|
618
|
+
const importKey = importValue || CDK$2.IMPORT.DATADOG_LOG_FORWARDER;
|
|
260
619
|
// Create a cache key based on name and import
|
|
261
620
|
const cacheKey = `${functionName}:${importKey}`;
|
|
262
621
|
// Get or create scope cache
|
|
@@ -278,7 +637,7 @@ function resolveDatadogForwarderFunction(scope, options) {
|
|
|
278
637
|
|
|
279
638
|
function resolveDatadogLayers(scope, options = {}) {
|
|
280
639
|
const { datadogApiKeyArn, uniqueId } = options;
|
|
281
|
-
let resolvedRegion = cdk
|
|
640
|
+
let resolvedRegion = cdk.Stack.of(scope).region || "us-east-1";
|
|
282
641
|
// Resolve the Datadog API key ARN from multiple sources
|
|
283
642
|
const resolvedDatadogApiKeyArn = datadogApiKeyArn ||
|
|
284
643
|
process.env.DATADOG_API_KEY_ARN ||
|
|
@@ -289,9 +648,9 @@ function resolveDatadogLayers(scope, options = {}) {
|
|
|
289
648
|
}
|
|
290
649
|
const layerIdSuffix = uniqueId || process.env.PROJECT_NONCE || Date.now().toString();
|
|
291
650
|
// Create Datadog Node.js layer
|
|
292
|
-
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}`);
|
|
293
652
|
// Create Datadog Extension layer
|
|
294
|
-
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}`);
|
|
295
654
|
return [datadogNodeLayer, datadogExtensionLayer];
|
|
296
655
|
}
|
|
297
656
|
|
|
@@ -303,7 +662,7 @@ function resolveDatadogLoggingDestination(scope, options) {
|
|
|
303
662
|
const { import: importValue, name } = options || {};
|
|
304
663
|
// Create a cache key based on name and import (same as forwarder function)
|
|
305
664
|
const functionName = name || DEFAULT_FUNCTION_NAME;
|
|
306
|
-
const importKey = importValue ||
|
|
665
|
+
const importKey = importValue || CDK$2.IMPORT.DATADOG_LOG_FORWARDER;
|
|
307
666
|
const cacheKey = `${functionName}:${importKey}`;
|
|
308
667
|
// Get or create scope cache
|
|
309
668
|
let scopeCache = destinationCache.get(scope);
|
|
@@ -326,7 +685,7 @@ function resolveDatadogLoggingDestination(scope, options) {
|
|
|
326
685
|
|
|
327
686
|
function resolveHostedZone(scope, { name = "HostedZone", zone = process.env.CDK_ENV_HOSTED_ZONE, } = {}) {
|
|
328
687
|
if (!zone) {
|
|
329
|
-
throw new
|
|
688
|
+
throw new errors.ConfigurationError("No `zone` provided. Set CDK_ENV_HOSTED_ZONE to use environment zone");
|
|
330
689
|
}
|
|
331
690
|
if (typeof zone === "string") {
|
|
332
691
|
return route53__namespace.HostedZone.fromLookup(scope, name, {
|
|
@@ -359,7 +718,7 @@ const resolveParamsAndSecrets = ({ paramsAndSecrets, options, }) => {
|
|
|
359
718
|
class JaypieApiGateway extends constructs.Construct {
|
|
360
719
|
constructor(scope, id, props) {
|
|
361
720
|
super(scope, id);
|
|
362
|
-
const { certificate = true, handler, host: propsHost, name, roleTag =
|
|
721
|
+
const { certificate = true, handler, host: propsHost, name, roleTag = CDK$2.ROLE.API, zone: propsZone, } = props;
|
|
363
722
|
// Determine zone from props or environment
|
|
364
723
|
let zone = propsZone;
|
|
365
724
|
if (!zone && process.env.CDK_ENV_API_HOSTED_ZONE) {
|
|
@@ -373,7 +732,7 @@ class JaypieApiGateway extends constructs.Construct {
|
|
|
373
732
|
}
|
|
374
733
|
else if (process.env.CDK_ENV_API_SUBDOMAIN &&
|
|
375
734
|
process.env.CDK_ENV_API_HOSTED_ZONE) {
|
|
376
|
-
host =
|
|
735
|
+
host = mergeDomain(process.env.CDK_ENV_API_SUBDOMAIN, process.env.CDK_ENV_API_HOSTED_ZONE);
|
|
377
736
|
}
|
|
378
737
|
}
|
|
379
738
|
const apiGatewayName = name || constructEnvName("ApiGateway");
|
|
@@ -388,7 +747,7 @@ class JaypieApiGateway extends constructs.Construct {
|
|
|
388
747
|
domainName: host,
|
|
389
748
|
validation: acm__namespace.CertificateValidation.fromDns(hostedZone),
|
|
390
749
|
});
|
|
391
|
-
cdk
|
|
750
|
+
cdk.Tags.of(certificateToUse).add(CDK$2.TAG.ROLE, CDK$2.ROLE.HOSTING);
|
|
392
751
|
}
|
|
393
752
|
else if (typeof certificate === "object") {
|
|
394
753
|
certificateToUse = certificate;
|
|
@@ -407,19 +766,19 @@ class JaypieApiGateway extends constructs.Construct {
|
|
|
407
766
|
handler,
|
|
408
767
|
...lambdaRestApiProps,
|
|
409
768
|
});
|
|
410
|
-
cdk
|
|
769
|
+
cdk.Tags.of(this._api).add(CDK$2.TAG.ROLE, roleTag);
|
|
411
770
|
if (host && certificateToUse && hostedZone) {
|
|
412
771
|
this._domainName = this._api.addDomainName(apiDomainName, {
|
|
413
772
|
domainName: host,
|
|
414
773
|
certificate: certificateToUse,
|
|
415
774
|
});
|
|
416
|
-
cdk
|
|
775
|
+
cdk.Tags.of(this._domainName).add(CDK$2.TAG.ROLE, roleTag);
|
|
417
776
|
const record = new route53__namespace.ARecord(this, "AliasRecord", {
|
|
418
777
|
recordName: host,
|
|
419
778
|
target: route53__namespace.RecordTarget.fromAlias(new route53Targets__namespace.ApiGatewayDomain(this._domainName)),
|
|
420
779
|
zone: hostedZone,
|
|
421
780
|
});
|
|
422
|
-
cdk
|
|
781
|
+
cdk.Tags.of(record).add(CDK$2.TAG.ROLE, CDK$2.ROLE.NETWORKING);
|
|
423
782
|
}
|
|
424
783
|
}
|
|
425
784
|
get api() {
|
|
@@ -460,8 +819,8 @@ class JaypieApiGateway extends constructs.Construct {
|
|
|
460
819
|
}
|
|
461
820
|
get env() {
|
|
462
821
|
return {
|
|
463
|
-
account: cdk
|
|
464
|
-
region: cdk
|
|
822
|
+
account: cdk.Stack.of(this).account,
|
|
823
|
+
region: cdk.Stack.of(this).region,
|
|
465
824
|
};
|
|
466
825
|
}
|
|
467
826
|
get stack() {
|
|
@@ -504,7 +863,7 @@ class JaypieApiGateway extends constructs.Construct {
|
|
|
504
863
|
}
|
|
505
864
|
}
|
|
506
865
|
|
|
507
|
-
class JaypieStack extends cdk
|
|
866
|
+
class JaypieStack extends cdk.Stack {
|
|
508
867
|
constructor(scope, id, props = {}) {
|
|
509
868
|
const { key, ...stackProps } = props;
|
|
510
869
|
// Handle stackName
|
|
@@ -537,7 +896,7 @@ class JaypieAppStack extends JaypieStack {
|
|
|
537
896
|
class JaypieLambda extends constructs.Construct {
|
|
538
897
|
constructor(scope, id, props) {
|
|
539
898
|
super(scope, id);
|
|
540
|
-
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;
|
|
541
900
|
// Get base environment with defaults
|
|
542
901
|
const environment = jaypieLambdaEnv({ initialEnvironment });
|
|
543
902
|
const codeAsset = typeof code === "string" ? lambda__namespace.Code.fromAsset(code) : code;
|
|
@@ -596,14 +955,14 @@ class JaypieLambda extends constructs.Construct {
|
|
|
596
955
|
runtime,
|
|
597
956
|
runtimeManagementMode,
|
|
598
957
|
securityGroups,
|
|
599
|
-
timeout: typeof timeout === "number" ? cdk
|
|
958
|
+
timeout: typeof timeout === "number" ? cdk.Duration.seconds(timeout) : timeout,
|
|
600
959
|
tracing,
|
|
601
960
|
vpc,
|
|
602
961
|
vpcSubnets,
|
|
603
962
|
// Enable auto-publishing of versions when using provisioned concurrency
|
|
604
963
|
currentVersionOptions: provisionedConcurrentExecutions !== undefined
|
|
605
964
|
? {
|
|
606
|
-
removalPolicy: cdk
|
|
965
|
+
removalPolicy: cdk.RemovalPolicy.RETAIN,
|
|
607
966
|
description: "Auto-published version for provisioned concurrency",
|
|
608
967
|
// Don't set provisioned concurrency here - it will be set on the alias
|
|
609
968
|
}
|
|
@@ -632,10 +991,10 @@ class JaypieLambda extends constructs.Construct {
|
|
|
632
991
|
this._provisioned.node.addDependency(version);
|
|
633
992
|
}
|
|
634
993
|
if (roleTag) {
|
|
635
|
-
cdk
|
|
994
|
+
cdk.Tags.of(this._lambda).add(CDK$2.TAG.ROLE, roleTag);
|
|
636
995
|
}
|
|
637
996
|
if (vendorTag) {
|
|
638
|
-
cdk
|
|
997
|
+
cdk.Tags.of(this._lambda).add(CDK$2.TAG.VENDOR, vendorTag);
|
|
639
998
|
}
|
|
640
999
|
// Assign _reference based on provisioned state
|
|
641
1000
|
this._reference =
|
|
@@ -738,8 +1097,8 @@ class JaypieLambda extends constructs.Construct {
|
|
|
738
1097
|
}
|
|
739
1098
|
get env() {
|
|
740
1099
|
return {
|
|
741
|
-
account: cdk
|
|
742
|
-
region: cdk
|
|
1100
|
+
account: cdk.Stack.of(this).account,
|
|
1101
|
+
region: cdk.Stack.of(this).region,
|
|
743
1102
|
};
|
|
744
1103
|
}
|
|
745
1104
|
get stack() {
|
|
@@ -756,19 +1115,19 @@ class JaypieLambda extends constructs.Construct {
|
|
|
756
1115
|
class JaypieQueuedLambda extends constructs.Construct {
|
|
757
1116
|
constructor(scope, id, props) {
|
|
758
1117
|
super(scope, id);
|
|
759
|
-
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;
|
|
760
1119
|
// Create SQS Queue
|
|
761
1120
|
this._queue = new sqs__namespace.Queue(this, "Queue", {
|
|
762
1121
|
fifo,
|
|
763
1122
|
visibilityTimeout: typeof visibilityTimeout === "number"
|
|
764
|
-
? cdk
|
|
1123
|
+
? cdk.Duration.seconds(visibilityTimeout)
|
|
765
1124
|
: visibilityTimeout,
|
|
766
1125
|
});
|
|
767
1126
|
if (roleTag) {
|
|
768
|
-
cdk
|
|
1127
|
+
cdk.Tags.of(this._queue).add(CDK$2.TAG.ROLE, roleTag);
|
|
769
1128
|
}
|
|
770
1129
|
if (vendorTag) {
|
|
771
|
-
cdk
|
|
1130
|
+
cdk.Tags.of(this._queue).add(CDK$2.TAG.VENDOR, vendorTag);
|
|
772
1131
|
}
|
|
773
1132
|
// Create Lambda with JaypieLambda
|
|
774
1133
|
this._lambdaConstruct = new JaypieLambda(this, "Function", {
|
|
@@ -913,12 +1272,12 @@ class JaypieQueuedLambda extends constructs.Construct {
|
|
|
913
1272
|
}
|
|
914
1273
|
get env() {
|
|
915
1274
|
return {
|
|
916
|
-
account: cdk
|
|
917
|
-
region: cdk
|
|
1275
|
+
account: cdk.Stack.of(this).account,
|
|
1276
|
+
region: cdk.Stack.of(this).region,
|
|
918
1277
|
};
|
|
919
1278
|
}
|
|
920
1279
|
get stack() {
|
|
921
|
-
return cdk
|
|
1280
|
+
return cdk.Stack.of(this);
|
|
922
1281
|
}
|
|
923
1282
|
applyRemovalPolicy(policy) {
|
|
924
1283
|
this._lambdaConstruct.applyRemovalPolicy(policy);
|
|
@@ -996,15 +1355,15 @@ class JaypieBucketQueuedLambda extends JaypieQueuedLambda {
|
|
|
996
1355
|
// Create S3 Bucket
|
|
997
1356
|
this._bucket = new s3__namespace.Bucket(this, "Bucket", {
|
|
998
1357
|
bucketName: bucketOptions.bucketName || bucketName,
|
|
999
|
-
removalPolicy: bucketOptions.removalPolicy || cdk
|
|
1358
|
+
removalPolicy: bucketOptions.removalPolicy || cdk.RemovalPolicy.RETAIN,
|
|
1000
1359
|
...bucketOptions,
|
|
1001
1360
|
});
|
|
1002
1361
|
// Add tags to bucket
|
|
1003
1362
|
if (roleTag) {
|
|
1004
|
-
cdk
|
|
1363
|
+
cdk.Tags.of(this._bucket).add(CDK$2.TAG.ROLE, roleTag);
|
|
1005
1364
|
}
|
|
1006
1365
|
if (vendorTag) {
|
|
1007
|
-
cdk
|
|
1366
|
+
cdk.Tags.of(this._bucket).add(CDK$2.TAG.VENDOR, vendorTag);
|
|
1008
1367
|
}
|
|
1009
1368
|
// Add an event notification from the bucket to the queue
|
|
1010
1369
|
this._bucket.addEventNotification(s3__namespace.EventType.OBJECT_CREATED, new s3n__namespace.SqsDestination(this.queue));
|
|
@@ -1127,15 +1486,184 @@ class JaypieBucketQueuedLambda extends JaypieQueuedLambda {
|
|
|
1127
1486
|
}
|
|
1128
1487
|
}
|
|
1129
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
|
+
|
|
1130
1658
|
// It is a consumer if the environment is ephemeral
|
|
1131
1659
|
function checkEnvIsConsumer(env = process.env) {
|
|
1132
|
-
return (env.PROJECT_ENV ===
|
|
1660
|
+
return (env.PROJECT_ENV === CDK$2.ENV.PERSONAL ||
|
|
1133
1661
|
!!env.CDK_ENV_PERSONAL ||
|
|
1134
1662
|
/** @deprecated */ env.PROJECT_ENV === "ephemeral" ||
|
|
1135
1663
|
/** @deprecated */ !!env.CDK_ENV_EPHEMERAL);
|
|
1136
1664
|
}
|
|
1137
1665
|
function checkEnvIsProvider(env = process.env) {
|
|
1138
|
-
return env.PROJECT_ENV ===
|
|
1666
|
+
return env.PROJECT_ENV === CDK$2.ENV.SANDBOX;
|
|
1139
1667
|
}
|
|
1140
1668
|
function cleanName(name) {
|
|
1141
1669
|
return name.replace(/[^a-zA-Z0-9:-]/g, "");
|
|
@@ -1149,7 +1677,7 @@ function exportEnvName(name, env = process.env) {
|
|
|
1149
1677
|
}
|
|
1150
1678
|
else {
|
|
1151
1679
|
if (checkEnvIsConsumer(env)) {
|
|
1152
|
-
rawName = `env-${
|
|
1680
|
+
rawName = `env-${CDK$2.ENV.SANDBOX}-${env.PROJECT_KEY}-${name}`;
|
|
1153
1681
|
}
|
|
1154
1682
|
else {
|
|
1155
1683
|
rawName = `env-${env.PROJECT_ENV}-${env.PROJECT_KEY}-${name}`;
|
|
@@ -1170,10 +1698,10 @@ class JaypieEnvSecret extends constructs.Construct {
|
|
|
1170
1698
|
exportName = cleanName(exportParam);
|
|
1171
1699
|
}
|
|
1172
1700
|
if (consumer) {
|
|
1173
|
-
const secretName = cdk
|
|
1701
|
+
const secretName = cdk.Fn.importValue(exportName);
|
|
1174
1702
|
this._secret = secretsmanager__namespace.Secret.fromSecretNameV2(this, id, secretName);
|
|
1175
1703
|
// Add CfnOutput for consumer secrets
|
|
1176
|
-
new cdk
|
|
1704
|
+
new cdk.CfnOutput(this, `ConsumedName`, {
|
|
1177
1705
|
value: this._secret.secretName,
|
|
1178
1706
|
});
|
|
1179
1707
|
}
|
|
@@ -1182,24 +1710,24 @@ class JaypieEnvSecret extends constructs.Construct {
|
|
|
1182
1710
|
const secretProps = {
|
|
1183
1711
|
generateSecretString,
|
|
1184
1712
|
secretStringValue: !generateSecretString && secretValue
|
|
1185
|
-
? cdk
|
|
1713
|
+
? cdk.SecretValue.unsafePlainText(secretValue)
|
|
1186
1714
|
: undefined,
|
|
1187
1715
|
};
|
|
1188
1716
|
this._secret = new secretsmanager__namespace.Secret(this, id, secretProps);
|
|
1189
1717
|
if (roleTag) {
|
|
1190
|
-
cdk
|
|
1718
|
+
cdk.Tags.of(this._secret).add(CDK$2.TAG.ROLE, roleTag);
|
|
1191
1719
|
}
|
|
1192
1720
|
if (vendorTag) {
|
|
1193
|
-
cdk
|
|
1721
|
+
cdk.Tags.of(this._secret).add(CDK$2.TAG.VENDOR, vendorTag);
|
|
1194
1722
|
}
|
|
1195
1723
|
if (provider) {
|
|
1196
|
-
new cdk
|
|
1724
|
+
new cdk.CfnOutput(this, `ProvidedName`, {
|
|
1197
1725
|
value: this._secret.secretName,
|
|
1198
1726
|
exportName,
|
|
1199
1727
|
});
|
|
1200
1728
|
}
|
|
1201
1729
|
else {
|
|
1202
|
-
new cdk
|
|
1730
|
+
new cdk.CfnOutput(this, `CreatedName`, {
|
|
1203
1731
|
value: this._secret.secretName,
|
|
1204
1732
|
});
|
|
1205
1733
|
}
|
|
@@ -1207,12 +1735,12 @@ class JaypieEnvSecret extends constructs.Construct {
|
|
|
1207
1735
|
}
|
|
1208
1736
|
// IResource implementation
|
|
1209
1737
|
get stack() {
|
|
1210
|
-
return cdk
|
|
1738
|
+
return cdk.Stack.of(this);
|
|
1211
1739
|
}
|
|
1212
1740
|
get env() {
|
|
1213
1741
|
return {
|
|
1214
|
-
account: cdk
|
|
1215
|
-
region: cdk
|
|
1742
|
+
account: cdk.Stack.of(this).account,
|
|
1743
|
+
region: cdk.Stack.of(this).region,
|
|
1216
1744
|
};
|
|
1217
1745
|
}
|
|
1218
1746
|
applyRemovalPolicy(policy) {
|
|
@@ -1264,8 +1792,8 @@ class JaypieDatadogSecret extends JaypieEnvSecret {
|
|
|
1264
1792
|
constructor(scope, id = "MongoConnectionString", props) {
|
|
1265
1793
|
const defaultProps = {
|
|
1266
1794
|
envKey: "DATADOG_API_KEY",
|
|
1267
|
-
roleTag:
|
|
1268
|
-
vendorTag:
|
|
1795
|
+
roleTag: CDK$2.ROLE.MONITORING,
|
|
1796
|
+
vendorTag: CDK$2.VENDOR.DATADOG,
|
|
1269
1797
|
...props,
|
|
1270
1798
|
};
|
|
1271
1799
|
super(scope, id, defaultProps);
|
|
@@ -1276,7 +1804,7 @@ class JaypieDnsRecord extends constructs.Construct {
|
|
|
1276
1804
|
constructor(scope, id, props) {
|
|
1277
1805
|
super(scope, id);
|
|
1278
1806
|
const { comment, recordName, type, values } = props;
|
|
1279
|
-
const ttl = props.ttl || cdk__namespace.Duration.seconds(
|
|
1807
|
+
const ttl = props.ttl || cdk__namespace.Duration.seconds(CDK$2.DNS.CONFIG.TTL);
|
|
1280
1808
|
// Resolve the hosted zone (supports both string and IHostedZone)
|
|
1281
1809
|
const zone = resolveHostedZone(scope, {
|
|
1282
1810
|
name: `${id}HostedZone`,
|
|
@@ -1291,9 +1819,9 @@ class JaypieDnsRecord extends constructs.Construct {
|
|
|
1291
1819
|
};
|
|
1292
1820
|
// Create the appropriate record based on type
|
|
1293
1821
|
switch (type) {
|
|
1294
|
-
case
|
|
1822
|
+
case CDK$2.DNS.RECORD.A: {
|
|
1295
1823
|
if (!Array.isArray(values) || values.length === 0) {
|
|
1296
|
-
throw new
|
|
1824
|
+
throw new errors.ConfigurationError("A record requires at least one IP address");
|
|
1297
1825
|
}
|
|
1298
1826
|
this.record = new route53.ARecord(this, "Record", {
|
|
1299
1827
|
...baseProps,
|
|
@@ -1301,9 +1829,9 @@ class JaypieDnsRecord extends constructs.Construct {
|
|
|
1301
1829
|
});
|
|
1302
1830
|
break;
|
|
1303
1831
|
}
|
|
1304
|
-
case
|
|
1832
|
+
case CDK$2.DNS.RECORD.CNAME: {
|
|
1305
1833
|
if (!Array.isArray(values) || values.length === 0) {
|
|
1306
|
-
throw new
|
|
1834
|
+
throw new errors.ConfigurationError("CNAME record requires a domain name");
|
|
1307
1835
|
}
|
|
1308
1836
|
this.record = new route53.CnameRecord(this, "Record", {
|
|
1309
1837
|
...baseProps,
|
|
@@ -1311,9 +1839,9 @@ class JaypieDnsRecord extends constructs.Construct {
|
|
|
1311
1839
|
});
|
|
1312
1840
|
break;
|
|
1313
1841
|
}
|
|
1314
|
-
case
|
|
1842
|
+
case CDK$2.DNS.RECORD.MX: {
|
|
1315
1843
|
if (!Array.isArray(values) || values.length === 0) {
|
|
1316
|
-
throw new
|
|
1844
|
+
throw new errors.ConfigurationError("MX record requires at least one mail server");
|
|
1317
1845
|
}
|
|
1318
1846
|
this.record = new route53.MxRecord(this, "Record", {
|
|
1319
1847
|
...baseProps,
|
|
@@ -1321,9 +1849,9 @@ class JaypieDnsRecord extends constructs.Construct {
|
|
|
1321
1849
|
});
|
|
1322
1850
|
break;
|
|
1323
1851
|
}
|
|
1324
|
-
case
|
|
1852
|
+
case CDK$2.DNS.RECORD.NS: {
|
|
1325
1853
|
if (!Array.isArray(values) || values.length === 0) {
|
|
1326
|
-
throw new
|
|
1854
|
+
throw new errors.ConfigurationError("NS record requires at least one name server");
|
|
1327
1855
|
}
|
|
1328
1856
|
this.record = new route53.NsRecord(this, "Record", {
|
|
1329
1857
|
...baseProps,
|
|
@@ -1331,9 +1859,9 @@ class JaypieDnsRecord extends constructs.Construct {
|
|
|
1331
1859
|
});
|
|
1332
1860
|
break;
|
|
1333
1861
|
}
|
|
1334
|
-
case
|
|
1862
|
+
case CDK$2.DNS.RECORD.TXT: {
|
|
1335
1863
|
if (!Array.isArray(values) || values.length === 0) {
|
|
1336
|
-
throw new
|
|
1864
|
+
throw new errors.ConfigurationError("TXT record requires at least one value");
|
|
1337
1865
|
}
|
|
1338
1866
|
this.record = new route53.TxtRecord(this, "Record", {
|
|
1339
1867
|
...baseProps,
|
|
@@ -1342,26 +1870,106 @@ class JaypieDnsRecord extends constructs.Construct {
|
|
|
1342
1870
|
break;
|
|
1343
1871
|
}
|
|
1344
1872
|
default:
|
|
1345
|
-
throw new
|
|
1873
|
+
throw new errors.ConfigurationError(`Unsupported DNS record type: ${type}. Supported types: A, CNAME, MX, NS, TXT`);
|
|
1346
1874
|
}
|
|
1347
1875
|
// Add standard tags to the DNS record
|
|
1348
|
-
cdk__namespace.Tags.of(this.record).add(
|
|
1349
|
-
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
|
+
}
|
|
1350
1958
|
}
|
|
1351
1959
|
}
|
|
1352
1960
|
|
|
1353
1961
|
class JaypieGitHubDeployRole extends constructs.Construct {
|
|
1354
1962
|
constructor(scope, id = "GitHubDeployRole", props = {}) {
|
|
1355
1963
|
super(scope, id);
|
|
1356
|
-
const { oidcProviderArn = cdk
|
|
1964
|
+
const { oidcProviderArn = cdk.Fn.importValue(CDK$2.IMPORT.OIDC_PROVIDER), output = true, repoRestriction: propsRepoRestriction, } = props;
|
|
1357
1965
|
// Extract account ID from the scope
|
|
1358
|
-
const accountId = cdk
|
|
1966
|
+
const accountId = cdk.Stack.of(this).account;
|
|
1359
1967
|
// Resolve repoRestriction from props or environment variables
|
|
1360
1968
|
let repoRestriction = propsRepoRestriction;
|
|
1361
1969
|
if (!repoRestriction) {
|
|
1362
1970
|
const envRepo = process.env.CDK_ENV_REPO || process.env.PROJECT_REPO;
|
|
1363
1971
|
if (!envRepo) {
|
|
1364
|
-
throw new
|
|
1972
|
+
throw new errors.ConfigurationError("No repoRestriction provided. Set repoRestriction prop, CDK_ENV_REPO, or PROJECT_REPO environment variable");
|
|
1365
1973
|
}
|
|
1366
1974
|
// Extract organization from owner/repo format and create org-wide restriction
|
|
1367
1975
|
const organization = envRepo.split("/")[0];
|
|
@@ -1374,10 +1982,10 @@ class JaypieGitHubDeployRole extends constructs.Construct {
|
|
|
1374
1982
|
"token.actions.githubusercontent.com:sub": repoRestriction,
|
|
1375
1983
|
},
|
|
1376
1984
|
}, "sts:AssumeRoleWithWebIdentity"),
|
|
1377
|
-
maxSessionDuration: cdk
|
|
1985
|
+
maxSessionDuration: cdk.Duration.hours(1),
|
|
1378
1986
|
path: "/",
|
|
1379
1987
|
});
|
|
1380
|
-
cdk
|
|
1988
|
+
cdk.Tags.of(this._role).add(CDK$2.TAG.ROLE, CDK$2.ROLE.DEPLOY);
|
|
1381
1989
|
// Allow the role to access the GitHub OIDC provider
|
|
1382
1990
|
this._role.addToPolicy(new awsIam.PolicyStatement({
|
|
1383
1991
|
actions: ["sts:AssumeRoleWithWebIdentity"],
|
|
@@ -1416,7 +2024,7 @@ class JaypieGitHubDeployRole extends constructs.Construct {
|
|
|
1416
2024
|
// Export the ARN of the role
|
|
1417
2025
|
if (output !== false) {
|
|
1418
2026
|
const outputId = typeof output === "string" ? output : "GitHubActionsRoleArn";
|
|
1419
|
-
new cdk
|
|
2027
|
+
new cdk.CfnOutput(this, outputId, {
|
|
1420
2028
|
value: this._role.roleArn,
|
|
1421
2029
|
});
|
|
1422
2030
|
}
|
|
@@ -1435,8 +2043,8 @@ class JaypieGitHubDeployRole extends constructs.Construct {
|
|
|
1435
2043
|
class JaypieExpressLambda extends JaypieLambda {
|
|
1436
2044
|
constructor(scope, id, props) {
|
|
1437
2045
|
super(scope, id, {
|
|
1438
|
-
timeout: cdk
|
|
1439
|
-
roleTag:
|
|
2046
|
+
timeout: cdk.Duration.seconds(CDK$2.DURATION.EXPRESS_API),
|
|
2047
|
+
roleTag: CDK$2.ROLE.API,
|
|
1440
2048
|
...props,
|
|
1441
2049
|
});
|
|
1442
2050
|
}
|
|
@@ -1496,7 +2104,7 @@ class JaypieHostedZone extends constructs.Construct {
|
|
|
1496
2104
|
super(scope, id);
|
|
1497
2105
|
const { zoneName, project } = props;
|
|
1498
2106
|
const destination = props.destination ?? true;
|
|
1499
|
-
const service = props.service ||
|
|
2107
|
+
const service = props.service || CDK$2.SERVICE.INFRASTRUCTURE;
|
|
1500
2108
|
// Create the log group
|
|
1501
2109
|
this.logGroup = new awsLogs.LogGroup(this, "LogGroup", {
|
|
1502
2110
|
logGroupName: process.env.PROJECT_NONCE
|
|
@@ -1505,10 +2113,10 @@ class JaypieHostedZone extends constructs.Construct {
|
|
|
1505
2113
|
retention: awsLogs.RetentionDays.ONE_WEEK,
|
|
1506
2114
|
});
|
|
1507
2115
|
// Add tags
|
|
1508
|
-
cdk__namespace.Tags.of(this.logGroup).add(
|
|
1509
|
-
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);
|
|
1510
2118
|
if (project) {
|
|
1511
|
-
cdk__namespace.Tags.of(this.logGroup).add(
|
|
2119
|
+
cdk__namespace.Tags.of(this.logGroup).add(CDK$2.TAG.PROJECT, project);
|
|
1512
2120
|
}
|
|
1513
2121
|
// Grant Route 53 permissions to write to the log group
|
|
1514
2122
|
this.logGroup.grantWrite(new awsIam.ServicePrincipal(SERVICE.ROUTE53));
|
|
@@ -1528,10 +2136,10 @@ class JaypieHostedZone extends constructs.Construct {
|
|
|
1528
2136
|
zoneName,
|
|
1529
2137
|
});
|
|
1530
2138
|
// Add tags
|
|
1531
|
-
cdk__namespace.Tags.of(this.hostedZone).add(
|
|
1532
|
-
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);
|
|
1533
2141
|
if (project) {
|
|
1534
|
-
cdk__namespace.Tags.of(this.hostedZone).add(
|
|
2142
|
+
cdk__namespace.Tags.of(this.hostedZone).add(CDK$2.TAG.PROJECT, project);
|
|
1535
2143
|
}
|
|
1536
2144
|
// Create DNS records if provided
|
|
1537
2145
|
this.dnsRecords = [];
|
|
@@ -1566,7 +2174,7 @@ class JaypieInfrastructureStack extends JaypieStack {
|
|
|
1566
2174
|
super(scope, id, { key, ...stackProps });
|
|
1567
2175
|
// Add infrastructure-specific tag
|
|
1568
2176
|
if (process.env.CDK_ENV_INFRASTRUCTURE_STACK_SHA) {
|
|
1569
|
-
cdk
|
|
2177
|
+
cdk.Tags.of(this).add(CDK.TAG.STACK_SHA, process.env.CDK_ENV_INFRASTRUCTURE_STACK_SHA);
|
|
1570
2178
|
}
|
|
1571
2179
|
}
|
|
1572
2180
|
}
|
|
@@ -1575,8 +2183,8 @@ class JaypieMongoDbSecret extends JaypieEnvSecret {
|
|
|
1575
2183
|
constructor(scope, id = "MongoConnectionString", props) {
|
|
1576
2184
|
const defaultProps = {
|
|
1577
2185
|
envKey: "MONGODB_URI",
|
|
1578
|
-
roleTag:
|
|
1579
|
-
vendorTag:
|
|
2186
|
+
roleTag: CDK$2.ROLE.STORAGE,
|
|
2187
|
+
vendorTag: CDK$2.VENDOR.MONGODB,
|
|
1580
2188
|
...props,
|
|
1581
2189
|
};
|
|
1582
2190
|
super(scope, id, defaultProps);
|
|
@@ -1587,14 +2195,108 @@ class JaypieOpenAiSecret extends JaypieEnvSecret {
|
|
|
1587
2195
|
constructor(scope, id = "OpenAiApiKey", props) {
|
|
1588
2196
|
const defaultProps = {
|
|
1589
2197
|
envKey: "OPENAI_API_KEY",
|
|
1590
|
-
roleTag:
|
|
1591
|
-
vendorTag:
|
|
2198
|
+
roleTag: CDK$2.ROLE.PROCESSING,
|
|
2199
|
+
vendorTag: CDK$2.VENDOR.OPENAI,
|
|
1592
2200
|
...props,
|
|
1593
2201
|
};
|
|
1594
2202
|
super(scope, id, defaultProps);
|
|
1595
2203
|
}
|
|
1596
2204
|
}
|
|
1597
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
|
+
|
|
1598
2300
|
/**
|
|
1599
2301
|
* JaypieSsoPermissions Construct
|
|
1600
2302
|
*
|
|
@@ -1661,15 +2363,15 @@ class JaypieSsoPermissions extends constructs.Construct {
|
|
|
1661
2363
|
.managedPolicyArn,
|
|
1662
2364
|
awsIam.ManagedPolicy.fromAwsManagedPolicyName("AWSManagementConsoleBasicUserAccess").managedPolicyArn,
|
|
1663
2365
|
],
|
|
1664
|
-
sessionDuration: cdk
|
|
2366
|
+
sessionDuration: cdk.Duration.hours(1).toIsoString(),
|
|
1665
2367
|
tags: [
|
|
1666
2368
|
{
|
|
1667
|
-
key:
|
|
1668
|
-
value:
|
|
2369
|
+
key: CDK$2.TAG.SERVICE,
|
|
2370
|
+
value: CDK$2.SERVICE.SSO,
|
|
1669
2371
|
},
|
|
1670
2372
|
{
|
|
1671
|
-
key:
|
|
1672
|
-
value:
|
|
2373
|
+
key: CDK$2.TAG.ROLE,
|
|
2374
|
+
value: CDK$2.ROLE.SECURITY,
|
|
1673
2375
|
},
|
|
1674
2376
|
],
|
|
1675
2377
|
});
|
|
@@ -1743,15 +2445,15 @@ class JaypieSsoPermissions extends constructs.Construct {
|
|
|
1743
2445
|
awsIam.ManagedPolicy.fromAwsManagedPolicyName("ReadOnlyAccess")
|
|
1744
2446
|
.managedPolicyArn,
|
|
1745
2447
|
],
|
|
1746
|
-
sessionDuration: cdk
|
|
2448
|
+
sessionDuration: cdk.Duration.hours(12).toIsoString(),
|
|
1747
2449
|
tags: [
|
|
1748
2450
|
{
|
|
1749
|
-
key:
|
|
1750
|
-
value:
|
|
2451
|
+
key: CDK$2.TAG.SERVICE,
|
|
2452
|
+
value: CDK$2.SERVICE.SSO,
|
|
1751
2453
|
},
|
|
1752
2454
|
{
|
|
1753
|
-
key:
|
|
1754
|
-
value:
|
|
2455
|
+
key: CDK$2.TAG.ROLE,
|
|
2456
|
+
value: CDK$2.ROLE.SECURITY,
|
|
1755
2457
|
},
|
|
1756
2458
|
],
|
|
1757
2459
|
});
|
|
@@ -1801,15 +2503,15 @@ class JaypieSsoPermissions extends constructs.Construct {
|
|
|
1801
2503
|
.managedPolicyArn,
|
|
1802
2504
|
awsIam.ManagedPolicy.fromAwsManagedPolicyName("job-function/SystemAdministrator").managedPolicyArn,
|
|
1803
2505
|
],
|
|
1804
|
-
sessionDuration: cdk
|
|
2506
|
+
sessionDuration: cdk.Duration.hours(4).toIsoString(),
|
|
1805
2507
|
tags: [
|
|
1806
2508
|
{
|
|
1807
|
-
key:
|
|
1808
|
-
value:
|
|
2509
|
+
key: CDK$2.TAG.SERVICE,
|
|
2510
|
+
value: CDK$2.SERVICE.SSO,
|
|
1809
2511
|
},
|
|
1810
2512
|
{
|
|
1811
|
-
key:
|
|
1812
|
-
value:
|
|
2513
|
+
key: CDK$2.TAG.ROLE,
|
|
2514
|
+
value: CDK$2.ROLE.SECURITY,
|
|
1813
2515
|
},
|
|
1814
2516
|
],
|
|
1815
2517
|
});
|
|
@@ -1841,19 +2543,19 @@ class JaypieSsoPermissions extends constructs.Construct {
|
|
|
1841
2543
|
permissionSetNames.forEach((permissionSetName) => {
|
|
1842
2544
|
const permissionSet = permissionSetMap[permissionSetName];
|
|
1843
2545
|
if (!permissionSet) {
|
|
1844
|
-
throw new
|
|
2546
|
+
throw new errors.ConfigurationError(`Unknown permission set: ${permissionSetName}. Valid options: ${Object.keys(permissionSetMap).join(", ")}`);
|
|
1845
2547
|
}
|
|
1846
2548
|
const accountAssignment = new awsSso.CfnAssignment(this, `AccountAssignment-${accountId}-${permissionSet.label}Role-${groupId}Group`, {
|
|
1847
2549
|
// Required
|
|
1848
2550
|
instanceArn: iamIdentityCenterArn,
|
|
1849
2551
|
permissionSetArn: permissionSet.arn,
|
|
1850
2552
|
principalId: groupId,
|
|
1851
|
-
principalType:
|
|
2553
|
+
principalType: CDK$2.PRINCIPAL_TYPE.GROUP,
|
|
1852
2554
|
targetId: accountId,
|
|
1853
|
-
targetType:
|
|
2555
|
+
targetType: CDK$2.TARGET_TYPE.AWS_ACCOUNT,
|
|
1854
2556
|
});
|
|
1855
|
-
cdk
|
|
1856
|
-
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);
|
|
1857
2559
|
});
|
|
1858
2560
|
});
|
|
1859
2561
|
};
|
|
@@ -1909,7 +2611,7 @@ class JaypieSsoSyncApplication extends constructs.Construct {
|
|
|
1909
2611
|
missingParams.push(`scimEndpointUrl or ${scimEndpointUrlEnvKey} environment variable`);
|
|
1910
2612
|
}
|
|
1911
2613
|
if (missingParams.length > 0) {
|
|
1912
|
-
throw new
|
|
2614
|
+
throw new errors.ConfigurationError(`JaypieSsoSyncApplication missing required configuration: ${missingParams.join(", ")}`);
|
|
1913
2615
|
}
|
|
1914
2616
|
// Create the SSO Sync Application
|
|
1915
2617
|
// Type assertion is safe because we validated all required values above
|
|
@@ -1923,18 +2625,18 @@ class JaypieSsoSyncApplication extends constructs.Construct {
|
|
|
1923
2625
|
GoogleCredentials: resolvedGoogleCredentials,
|
|
1924
2626
|
GoogleGroupMatch: resolvedGoogleGroupMatch,
|
|
1925
2627
|
IdentityStoreID: resolvedIdentityStoreId,
|
|
1926
|
-
Region: cdk
|
|
2628
|
+
Region: cdk.Stack.of(this).region,
|
|
1927
2629
|
SCIMEndpointAccessToken: resolvedScimEndpointAccessToken,
|
|
1928
2630
|
SCIMEndpointUrl: resolvedScimEndpointUrl,
|
|
1929
2631
|
},
|
|
1930
2632
|
});
|
|
1931
2633
|
// Add tags
|
|
1932
2634
|
const defaultTags = {
|
|
1933
|
-
[
|
|
2635
|
+
[CDK$2.TAG.ROLE]: CDK$2.ROLE.SECURITY,
|
|
1934
2636
|
};
|
|
1935
2637
|
const allTags = { ...defaultTags, ...tags };
|
|
1936
2638
|
Object.entries(allTags).forEach(([key, value]) => {
|
|
1937
|
-
cdk
|
|
2639
|
+
cdk.Tags.of(this._application).add(key, value);
|
|
1938
2640
|
});
|
|
1939
2641
|
}
|
|
1940
2642
|
get application() {
|
|
@@ -1946,8 +2648,8 @@ class JaypieTraceSigningKeySecret extends JaypieEnvSecret {
|
|
|
1946
2648
|
constructor(scope, id = "TraceSigningKey", props) {
|
|
1947
2649
|
const defaultProps = {
|
|
1948
2650
|
envKey: "TRACE_SIGNING_KEY",
|
|
1949
|
-
roleTag:
|
|
1950
|
-
vendorTag:
|
|
2651
|
+
roleTag: CDK$2.ROLE.API,
|
|
2652
|
+
vendorTag: CDK$2.VENDOR.KNOWTRACE,
|
|
1951
2653
|
...props,
|
|
1952
2654
|
};
|
|
1953
2655
|
super(scope, id, defaultProps);
|
|
@@ -1957,19 +2659,19 @@ class JaypieTraceSigningKeySecret extends JaypieEnvSecret {
|
|
|
1957
2659
|
class JaypieWebDeploymentBucket extends constructs.Construct {
|
|
1958
2660
|
constructor(scope, id, props = {}) {
|
|
1959
2661
|
super(scope, id);
|
|
1960
|
-
const roleTag = props.roleTag ||
|
|
2662
|
+
const roleTag = props.roleTag || CDK$2.ROLE.HOSTING;
|
|
1961
2663
|
// Environment variable validation
|
|
1962
2664
|
if (process.env.CDK_ENV_WEB_SUBDOMAIN &&
|
|
1963
|
-
!
|
|
1964
|
-
throw new
|
|
2665
|
+
!isValidSubdomain(process.env.CDK_ENV_WEB_SUBDOMAIN)) {
|
|
2666
|
+
throw new errors.ConfigurationError("CDK_ENV_WEB_SUBDOMAIN is not a valid subdomain");
|
|
1965
2667
|
}
|
|
1966
2668
|
if (process.env.CDK_ENV_WEB_HOSTED_ZONE &&
|
|
1967
|
-
!
|
|
1968
|
-
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");
|
|
1969
2671
|
}
|
|
1970
2672
|
if (process.env.CDK_ENV_HOSTED_ZONE &&
|
|
1971
|
-
!
|
|
1972
|
-
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");
|
|
1973
2675
|
}
|
|
1974
2676
|
// Determine host from props or environment
|
|
1975
2677
|
let host = props.host;
|
|
@@ -1977,7 +2679,7 @@ class JaypieWebDeploymentBucket extends constructs.Construct {
|
|
|
1977
2679
|
try {
|
|
1978
2680
|
host =
|
|
1979
2681
|
process.env.CDK_ENV_WEB_HOST ||
|
|
1980
|
-
|
|
2682
|
+
mergeDomain(process.env.CDK_ENV_WEB_SUBDOMAIN || "", process.env.CDK_ENV_WEB_HOSTED_ZONE ||
|
|
1981
2683
|
process.env.CDK_ENV_HOSTED_ZONE ||
|
|
1982
2684
|
"");
|
|
1983
2685
|
}
|
|
@@ -1985,8 +2687,8 @@ class JaypieWebDeploymentBucket extends constructs.Construct {
|
|
|
1985
2687
|
host = undefined;
|
|
1986
2688
|
}
|
|
1987
2689
|
}
|
|
1988
|
-
if (host && !
|
|
1989
|
-
throw new
|
|
2690
|
+
if (host && !isValidHostname$1(host)) {
|
|
2691
|
+
throw new errors.ConfigurationError("Host is not a valid hostname");
|
|
1990
2692
|
}
|
|
1991
2693
|
// Determine zone from props or environment
|
|
1992
2694
|
const zone = props.zone ||
|
|
@@ -1999,7 +2701,7 @@ class JaypieWebDeploymentBucket extends constructs.Construct {
|
|
|
1999
2701
|
blockPublicAccess: s3__namespace.BlockPublicAccess.BLOCK_ACLS,
|
|
2000
2702
|
bucketName: props.name || constructEnvName("web"),
|
|
2001
2703
|
publicReadAccess: true,
|
|
2002
|
-
removalPolicy: cdk
|
|
2704
|
+
removalPolicy: cdk.RemovalPolicy.DESTROY,
|
|
2003
2705
|
versioned: false,
|
|
2004
2706
|
websiteErrorDocument: "index.html",
|
|
2005
2707
|
websiteIndexDocument: "index.html",
|
|
@@ -2017,7 +2719,7 @@ class JaypieWebDeploymentBucket extends constructs.Construct {
|
|
|
2017
2719
|
this.isWebsite = this.bucket.isWebsite;
|
|
2018
2720
|
this.notificationsHandlerRole = undefined;
|
|
2019
2721
|
this.policy = this.bucket.policy;
|
|
2020
|
-
cdk
|
|
2722
|
+
cdk.Tags.of(this.bucket).add(CDK$2.TAG.ROLE, roleTag);
|
|
2021
2723
|
// Create deployment role if repository is configured
|
|
2022
2724
|
let repo;
|
|
2023
2725
|
if (process.env.CDK_ENV_REPO) {
|
|
@@ -2025,14 +2727,14 @@ class JaypieWebDeploymentBucket extends constructs.Construct {
|
|
|
2025
2727
|
}
|
|
2026
2728
|
if (repo) {
|
|
2027
2729
|
const bucketDeployRole = new awsIam.Role(this, "DestinationBucketDeployRole", {
|
|
2028
|
-
assumedBy: new awsIam.FederatedPrincipal(cdk
|
|
2730
|
+
assumedBy: new awsIam.FederatedPrincipal(cdk.Fn.importValue(CDK$2.IMPORT.OIDC_PROVIDER), {
|
|
2029
2731
|
StringLike: {
|
|
2030
2732
|
"token.actions.githubusercontent.com:sub": repo,
|
|
2031
2733
|
},
|
|
2032
2734
|
}, "sts:AssumeRoleWithWebIdentity"),
|
|
2033
|
-
maxSessionDuration: cdk
|
|
2735
|
+
maxSessionDuration: cdk.Duration.hours(1),
|
|
2034
2736
|
});
|
|
2035
|
-
cdk
|
|
2737
|
+
cdk.Tags.of(bucketDeployRole).add(CDK$2.TAG.ROLE, CDK$2.ROLE.DEPLOY);
|
|
2036
2738
|
// Allow the role to write to the bucket
|
|
2037
2739
|
bucketDeployRole.addToPolicy(new awsIam.PolicyStatement({
|
|
2038
2740
|
effect: awsIam.Effect.ALLOW,
|
|
@@ -2057,7 +2759,7 @@ class JaypieWebDeploymentBucket extends constructs.Construct {
|
|
|
2057
2759
|
}));
|
|
2058
2760
|
this.deployRoleArn = bucketDeployRole.roleArn;
|
|
2059
2761
|
// Output the deploy role ARN
|
|
2060
|
-
new cdk
|
|
2762
|
+
new cdk.CfnOutput(this, "DestinationBucketDeployRoleArn", {
|
|
2061
2763
|
value: bucketDeployRole.roleArn,
|
|
2062
2764
|
});
|
|
2063
2765
|
}
|
|
@@ -2077,10 +2779,10 @@ class JaypieWebDeploymentBucket extends constructs.Construct {
|
|
|
2077
2779
|
domainName: host,
|
|
2078
2780
|
validation: acm__namespace.CertificateValidation.fromDns(hostedZone),
|
|
2079
2781
|
});
|
|
2080
|
-
new cdk
|
|
2782
|
+
new cdk.CfnOutput(this, "CertificateArn", {
|
|
2081
2783
|
value: this.certificate.certificateArn,
|
|
2082
2784
|
});
|
|
2083
|
-
cdk
|
|
2785
|
+
cdk.Tags.of(this.certificate).add(CDK$2.TAG.ROLE, roleTag);
|
|
2084
2786
|
}
|
|
2085
2787
|
// Create CloudFront distribution
|
|
2086
2788
|
this.distribution = new cloudfront__namespace.Distribution(this, "Distribution", {
|
|
@@ -2092,7 +2794,7 @@ class JaypieWebDeploymentBucket extends constructs.Construct {
|
|
|
2092
2794
|
certificate: this.certificate,
|
|
2093
2795
|
domainNames: [host],
|
|
2094
2796
|
});
|
|
2095
|
-
cdk
|
|
2797
|
+
cdk.Tags.of(this.distribution).add(CDK$2.TAG.ROLE, roleTag);
|
|
2096
2798
|
// If this is production, enable caching on everything but index.html
|
|
2097
2799
|
if (isProductionEnv()) {
|
|
2098
2800
|
this.distribution.addBehavior("/*", new origins__namespace.S3Origin(this.bucket), {
|
|
@@ -2106,7 +2808,7 @@ class JaypieWebDeploymentBucket extends constructs.Construct {
|
|
|
2106
2808
|
target: route53__namespace.RecordTarget.fromAlias(new route53Targets__namespace.CloudFrontTarget(this.distribution)),
|
|
2107
2809
|
zone: hostedZone,
|
|
2108
2810
|
});
|
|
2109
|
-
cdk
|
|
2811
|
+
cdk.Tags.of(record).add(CDK$2.TAG.ROLE, CDK$2.ROLE.NETWORKING);
|
|
2110
2812
|
this.distributionDomainName = this.distribution.distributionDomainName;
|
|
2111
2813
|
}
|
|
2112
2814
|
}
|
|
@@ -2208,16 +2910,17 @@ class JaypieWebDeploymentBucket extends constructs.Construct {
|
|
|
2208
2910
|
}
|
|
2209
2911
|
}
|
|
2210
2912
|
|
|
2211
|
-
|
|
2212
|
-
|
|
2213
|
-
get: function () { return cdk.CDK; }
|
|
2214
|
-
});
|
|
2913
|
+
exports.CDK = CDK$2;
|
|
2914
|
+
exports.JaypieAccountLoggingBucket = JaypieAccountLoggingBucket;
|
|
2215
2915
|
exports.JaypieApiGateway = JaypieApiGateway;
|
|
2216
2916
|
exports.JaypieAppStack = JaypieAppStack;
|
|
2217
2917
|
exports.JaypieBucketQueuedLambda = JaypieBucketQueuedLambda;
|
|
2918
|
+
exports.JaypieDatadogBucket = JaypieDatadogBucket;
|
|
2919
|
+
exports.JaypieDatadogForwarder = JaypieDatadogForwarder;
|
|
2218
2920
|
exports.JaypieDatadogSecret = JaypieDatadogSecret;
|
|
2219
2921
|
exports.JaypieDnsRecord = JaypieDnsRecord;
|
|
2220
2922
|
exports.JaypieEnvSecret = JaypieEnvSecret;
|
|
2923
|
+
exports.JaypieEventsRule = JaypieEventsRule;
|
|
2221
2924
|
exports.JaypieExpressLambda = JaypieExpressLambda;
|
|
2222
2925
|
exports.JaypieGitHubDeployRole = JaypieGitHubDeployRole;
|
|
2223
2926
|
exports.JaypieHostedZone = JaypieHostedZone;
|
|
@@ -2225,6 +2928,7 @@ exports.JaypieInfrastructureStack = JaypieInfrastructureStack;
|
|
|
2225
2928
|
exports.JaypieLambda = JaypieLambda;
|
|
2226
2929
|
exports.JaypieMongoDbSecret = JaypieMongoDbSecret;
|
|
2227
2930
|
exports.JaypieOpenAiSecret = JaypieOpenAiSecret;
|
|
2931
|
+
exports.JaypieOrganizationTrail = JaypieOrganizationTrail;
|
|
2228
2932
|
exports.JaypieQueuedLambda = JaypieQueuedLambda;
|
|
2229
2933
|
exports.JaypieSsoPermissions = JaypieSsoPermissions;
|
|
2230
2934
|
exports.JaypieSsoSyncApplication = JaypieSsoSyncApplication;
|
|
@@ -2236,10 +2940,14 @@ exports.constructEnvName = constructEnvName;
|
|
|
2236
2940
|
exports.constructStackName = constructStackName;
|
|
2237
2941
|
exports.constructTagger = constructTagger;
|
|
2238
2942
|
exports.envHostname = envHostname;
|
|
2943
|
+
exports.extendDatadogRole = extendDatadogRole;
|
|
2239
2944
|
exports.isEnv = isEnv;
|
|
2240
2945
|
exports.isProductionEnv = isProductionEnv;
|
|
2241
2946
|
exports.isSandboxEnv = isSandboxEnv;
|
|
2947
|
+
exports.isValidHostname = isValidHostname$1;
|
|
2948
|
+
exports.isValidSubdomain = isValidSubdomain;
|
|
2242
2949
|
exports.jaypieLambdaEnv = jaypieLambdaEnv;
|
|
2950
|
+
exports.mergeDomain = mergeDomain;
|
|
2243
2951
|
exports.resolveDatadogForwarderFunction = resolveDatadogForwarderFunction;
|
|
2244
2952
|
exports.resolveDatadogLayers = resolveDatadogLayers;
|
|
2245
2953
|
exports.resolveDatadogLoggingDestination = resolveDatadogLoggingDestination;
|