@friggframework/devtools 2.0.0-next.43 → 2.0.0-next.45
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.
|
@@ -3,9 +3,14 @@ const fs = require('fs');
|
|
|
3
3
|
const { AWSDiscovery } = require('./aws-discovery');
|
|
4
4
|
|
|
5
5
|
const shouldRunDiscovery = (AppDefinition) => {
|
|
6
|
-
console.log(
|
|
6
|
+
console.log(
|
|
7
|
+
'⚙️ Checking FRIGG_SKIP_AWS_DISCOVERY:',
|
|
8
|
+
process.env.FRIGG_SKIP_AWS_DISCOVERY
|
|
9
|
+
);
|
|
7
10
|
if (process.env.FRIGG_SKIP_AWS_DISCOVERY === 'true') {
|
|
8
|
-
console.log(
|
|
11
|
+
console.log(
|
|
12
|
+
'⚙️ Skipping AWS discovery because FRIGG_SKIP_AWS_DISCOVERY is set.'
|
|
13
|
+
);
|
|
9
14
|
return false;
|
|
10
15
|
}
|
|
11
16
|
|
|
@@ -55,11 +60,16 @@ const getAppEnvironmentVars = (AppDefinition) => {
|
|
|
55
60
|
}
|
|
56
61
|
|
|
57
62
|
if (envKeys.length > 0) {
|
|
58
|
-
console.log(
|
|
63
|
+
console.log(
|
|
64
|
+
` Found ${envKeys.length} environment variables: ${envKeys.join(
|
|
65
|
+
', '
|
|
66
|
+
)}`
|
|
67
|
+
);
|
|
59
68
|
}
|
|
60
69
|
if (skippedKeys.length > 0) {
|
|
61
70
|
console.log(
|
|
62
|
-
` ⚠️ Skipped ${skippedKeys.length
|
|
71
|
+
` ⚠️ Skipped ${skippedKeys.length
|
|
72
|
+
} reserved AWS Lambda variables: ${skippedKeys.join(', ')}`
|
|
63
73
|
);
|
|
64
74
|
}
|
|
65
75
|
|
|
@@ -75,7 +85,9 @@ const findNodeModulesPath = () => {
|
|
|
75
85
|
const potentialPath = path.join(currentDir, 'node_modules');
|
|
76
86
|
if (fs.existsSync(potentialPath)) {
|
|
77
87
|
nodeModulesPath = potentialPath;
|
|
78
|
-
console.log(
|
|
88
|
+
console.log(
|
|
89
|
+
`Found node_modules at: ${nodeModulesPath} (method 1)`
|
|
90
|
+
);
|
|
79
91
|
break;
|
|
80
92
|
}
|
|
81
93
|
const parentDir = path.dirname(currentDir);
|
|
@@ -86,10 +98,14 @@ const findNodeModulesPath = () => {
|
|
|
86
98
|
if (!nodeModulesPath) {
|
|
87
99
|
try {
|
|
88
100
|
const { execSync } = require('node:child_process');
|
|
89
|
-
const npmRoot = execSync('npm root', {
|
|
101
|
+
const npmRoot = execSync('npm root', {
|
|
102
|
+
encoding: 'utf8',
|
|
103
|
+
}).trim();
|
|
90
104
|
if (fs.existsSync(npmRoot)) {
|
|
91
105
|
nodeModulesPath = npmRoot;
|
|
92
|
-
console.log(
|
|
106
|
+
console.log(
|
|
107
|
+
`Found node_modules at: ${nodeModulesPath} (method 2)`
|
|
108
|
+
);
|
|
93
109
|
}
|
|
94
110
|
} catch (npmError) {
|
|
95
111
|
console.error('Error executing npm root:', npmError);
|
|
@@ -101,10 +117,15 @@ const findNodeModulesPath = () => {
|
|
|
101
117
|
for (let i = 0; i < 5; i++) {
|
|
102
118
|
const packageJsonPath = path.join(currentDir, 'package.json');
|
|
103
119
|
if (fs.existsSync(packageJsonPath)) {
|
|
104
|
-
const potentialNodeModules = path.join(
|
|
120
|
+
const potentialNodeModules = path.join(
|
|
121
|
+
currentDir,
|
|
122
|
+
'node_modules'
|
|
123
|
+
);
|
|
105
124
|
if (fs.existsSync(potentialNodeModules)) {
|
|
106
125
|
nodeModulesPath = potentialNodeModules;
|
|
107
|
-
console.log(
|
|
126
|
+
console.log(
|
|
127
|
+
`Found node_modules at: ${nodeModulesPath} (method 3)`
|
|
128
|
+
);
|
|
108
129
|
break;
|
|
109
130
|
}
|
|
110
131
|
}
|
|
@@ -118,7 +139,9 @@ const findNodeModulesPath = () => {
|
|
|
118
139
|
return nodeModulesPath;
|
|
119
140
|
}
|
|
120
141
|
|
|
121
|
-
console.warn(
|
|
142
|
+
console.warn(
|
|
143
|
+
'Could not find node_modules path, falling back to default'
|
|
144
|
+
);
|
|
122
145
|
return path.resolve(process.cwd(), '../node_modules');
|
|
123
146
|
} catch (error) {
|
|
124
147
|
console.error('Error finding node_modules path:', error);
|
|
@@ -143,8 +166,13 @@ const modifyHandlerPaths = (functions) => {
|
|
|
143
166
|
const functionDef = modifiedFunctions[functionName];
|
|
144
167
|
if (functionDef?.handler?.includes('node_modules/')) {
|
|
145
168
|
const relativePath = path.relative(process.cwd(), nodeModulesPath);
|
|
146
|
-
functionDef.handler = functionDef.handler.replace(
|
|
147
|
-
|
|
169
|
+
functionDef.handler = functionDef.handler.replace(
|
|
170
|
+
'node_modules/',
|
|
171
|
+
`${relativePath}/`
|
|
172
|
+
);
|
|
173
|
+
console.log(
|
|
174
|
+
`Updated handler for ${functionName}: ${functionDef.handler}`
|
|
175
|
+
);
|
|
148
176
|
}
|
|
149
177
|
}
|
|
150
178
|
|
|
@@ -160,7 +188,10 @@ const createVPCInfrastructure = (AppDefinition) => {
|
|
|
160
188
|
EnableDnsHostnames: true,
|
|
161
189
|
EnableDnsSupport: true,
|
|
162
190
|
Tags: [
|
|
163
|
-
{
|
|
191
|
+
{
|
|
192
|
+
Key: 'Name',
|
|
193
|
+
Value: '${self:service}-${self:provider.stage}-vpc',
|
|
194
|
+
},
|
|
164
195
|
{ Key: 'ManagedBy', Value: 'Frigg' },
|
|
165
196
|
{ Key: 'Service', Value: '${self:service}' },
|
|
166
197
|
{ Key: 'Stage', Value: '${self:provider.stage}' },
|
|
@@ -171,7 +202,10 @@ const createVPCInfrastructure = (AppDefinition) => {
|
|
|
171
202
|
Type: 'AWS::EC2::InternetGateway',
|
|
172
203
|
Properties: {
|
|
173
204
|
Tags: [
|
|
174
|
-
{
|
|
205
|
+
{
|
|
206
|
+
Key: 'Name',
|
|
207
|
+
Value: '${self:service}-${self:provider.stage}-igw',
|
|
208
|
+
},
|
|
175
209
|
{ Key: 'ManagedBy', Value: 'Frigg' },
|
|
176
210
|
{ Key: 'Service', Value: '${self:service}' },
|
|
177
211
|
{ Key: 'Stage', Value: '${self:provider.stage}' },
|
|
@@ -193,7 +227,10 @@ const createVPCInfrastructure = (AppDefinition) => {
|
|
|
193
227
|
AvailabilityZone: { 'Fn::Select': [0, { 'Fn::GetAZs': '' }] },
|
|
194
228
|
MapPublicIpOnLaunch: true,
|
|
195
229
|
Tags: [
|
|
196
|
-
{
|
|
230
|
+
{
|
|
231
|
+
Key: 'Name',
|
|
232
|
+
Value: '${self:service}-${self:provider.stage}-public-subnet',
|
|
233
|
+
},
|
|
197
234
|
{ Key: 'ManagedBy', Value: 'Frigg' },
|
|
198
235
|
{ Key: 'Service', Value: '${self:service}' },
|
|
199
236
|
{ Key: 'Stage', Value: '${self:provider.stage}' },
|
|
@@ -208,7 +245,10 @@ const createVPCInfrastructure = (AppDefinition) => {
|
|
|
208
245
|
CidrBlock: '10.0.2.0/24',
|
|
209
246
|
AvailabilityZone: { 'Fn::Select': [0, { 'Fn::GetAZs': '' }] },
|
|
210
247
|
Tags: [
|
|
211
|
-
{
|
|
248
|
+
{
|
|
249
|
+
Key: 'Name',
|
|
250
|
+
Value: '${self:service}-${self:provider.stage}-private-subnet-1',
|
|
251
|
+
},
|
|
212
252
|
{ Key: 'ManagedBy', Value: 'Frigg' },
|
|
213
253
|
{ Key: 'Service', Value: '${self:service}' },
|
|
214
254
|
{ Key: 'Stage', Value: '${self:provider.stage}' },
|
|
@@ -223,7 +263,10 @@ const createVPCInfrastructure = (AppDefinition) => {
|
|
|
223
263
|
CidrBlock: '10.0.3.0/24',
|
|
224
264
|
AvailabilityZone: { 'Fn::Select': [1, { 'Fn::GetAZs': '' }] },
|
|
225
265
|
Tags: [
|
|
226
|
-
{
|
|
266
|
+
{
|
|
267
|
+
Key: 'Name',
|
|
268
|
+
Value: '${self:service}-${self:provider.stage}-private-subnet-2',
|
|
269
|
+
},
|
|
227
270
|
{ Key: 'ManagedBy', Value: 'Frigg' },
|
|
228
271
|
{ Key: 'Service', Value: '${self:service}' },
|
|
229
272
|
{ Key: 'Stage', Value: '${self:provider.stage}' },
|
|
@@ -236,7 +279,10 @@ const createVPCInfrastructure = (AppDefinition) => {
|
|
|
236
279
|
Properties: {
|
|
237
280
|
Domain: 'vpc',
|
|
238
281
|
Tags: [
|
|
239
|
-
{
|
|
282
|
+
{
|
|
283
|
+
Key: 'Name',
|
|
284
|
+
Value: '${self:service}-${self:provider.stage}-nat-eip',
|
|
285
|
+
},
|
|
240
286
|
{ Key: 'ManagedBy', Value: 'Frigg' },
|
|
241
287
|
{ Key: 'Service', Value: '${self:service}' },
|
|
242
288
|
{ Key: 'Stage', Value: '${self:provider.stage}' },
|
|
@@ -247,10 +293,15 @@ const createVPCInfrastructure = (AppDefinition) => {
|
|
|
247
293
|
FriggNATGateway: {
|
|
248
294
|
Type: 'AWS::EC2::NatGateway',
|
|
249
295
|
Properties: {
|
|
250
|
-
AllocationId: {
|
|
296
|
+
AllocationId: {
|
|
297
|
+
'Fn::GetAtt': ['FriggNATGatewayEIP', 'AllocationId'],
|
|
298
|
+
},
|
|
251
299
|
SubnetId: { Ref: 'FriggPublicSubnet' },
|
|
252
300
|
Tags: [
|
|
253
|
-
{
|
|
301
|
+
{
|
|
302
|
+
Key: 'Name',
|
|
303
|
+
Value: '${self:service}-${self:provider.stage}-nat-gateway',
|
|
304
|
+
},
|
|
254
305
|
{ Key: 'ManagedBy', Value: 'Frigg' },
|
|
255
306
|
{ Key: 'Service', Value: '${self:service}' },
|
|
256
307
|
{ Key: 'Stage', Value: '${self:provider.stage}' },
|
|
@@ -262,7 +313,10 @@ const createVPCInfrastructure = (AppDefinition) => {
|
|
|
262
313
|
Properties: {
|
|
263
314
|
VpcId: { Ref: 'FriggVPC' },
|
|
264
315
|
Tags: [
|
|
265
|
-
{
|
|
316
|
+
{
|
|
317
|
+
Key: 'Name',
|
|
318
|
+
Value: '${self:service}-${self:provider.stage}-public-rt',
|
|
319
|
+
},
|
|
266
320
|
{ Key: 'ManagedBy', Value: 'Frigg' },
|
|
267
321
|
{ Key: 'Service', Value: '${self:service}' },
|
|
268
322
|
{ Key: 'Stage', Value: '${self:provider.stage}' },
|
|
@@ -291,7 +345,10 @@ const createVPCInfrastructure = (AppDefinition) => {
|
|
|
291
345
|
Properties: {
|
|
292
346
|
VpcId: { Ref: 'FriggVPC' },
|
|
293
347
|
Tags: [
|
|
294
|
-
{
|
|
348
|
+
{
|
|
349
|
+
Key: 'Name',
|
|
350
|
+
Value: '${self:service}-${self:provider.stage}-private-rt',
|
|
351
|
+
},
|
|
295
352
|
{ Key: 'ManagedBy', Value: 'Frigg' },
|
|
296
353
|
{ Key: 'Service', Value: '${self:service}' },
|
|
297
354
|
{ Key: 'Stage', Value: '${self:provider.stage}' },
|
|
@@ -327,14 +384,47 @@ const createVPCInfrastructure = (AppDefinition) => {
|
|
|
327
384
|
GroupDescription: 'Security group for Frigg Lambda functions',
|
|
328
385
|
VpcId: { Ref: 'FriggVPC' },
|
|
329
386
|
SecurityGroupEgress: [
|
|
330
|
-
{
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
387
|
+
{
|
|
388
|
+
IpProtocol: 'tcp',
|
|
389
|
+
FromPort: 443,
|
|
390
|
+
ToPort: 443,
|
|
391
|
+
CidrIp: '0.0.0.0/0',
|
|
392
|
+
Description: 'HTTPS outbound',
|
|
393
|
+
},
|
|
394
|
+
{
|
|
395
|
+
IpProtocol: 'tcp',
|
|
396
|
+
FromPort: 80,
|
|
397
|
+
ToPort: 80,
|
|
398
|
+
CidrIp: '0.0.0.0/0',
|
|
399
|
+
Description: 'HTTP outbound',
|
|
400
|
+
},
|
|
401
|
+
{
|
|
402
|
+
IpProtocol: 'tcp',
|
|
403
|
+
FromPort: 53,
|
|
404
|
+
ToPort: 53,
|
|
405
|
+
CidrIp: '0.0.0.0/0',
|
|
406
|
+
Description: 'DNS TCP',
|
|
407
|
+
},
|
|
408
|
+
{
|
|
409
|
+
IpProtocol: 'udp',
|
|
410
|
+
FromPort: 53,
|
|
411
|
+
ToPort: 53,
|
|
412
|
+
CidrIp: '0.0.0.0/0',
|
|
413
|
+
Description: 'DNS UDP',
|
|
414
|
+
},
|
|
415
|
+
{
|
|
416
|
+
IpProtocol: 'tcp',
|
|
417
|
+
FromPort: 27017,
|
|
418
|
+
ToPort: 27017,
|
|
419
|
+
CidrIp: '0.0.0.0/0',
|
|
420
|
+
Description: 'MongoDB outbound',
|
|
421
|
+
},
|
|
335
422
|
],
|
|
336
423
|
Tags: [
|
|
337
|
-
{
|
|
424
|
+
{
|
|
425
|
+
Key: 'Name',
|
|
426
|
+
Value: '${self:service}-${self:provider.stage}-lambda-sg',
|
|
427
|
+
},
|
|
338
428
|
{ Key: 'ManagedBy', Value: 'Frigg' },
|
|
339
429
|
{ Key: 'Service', Value: '${self:service}' },
|
|
340
430
|
{ Key: 'Stage', Value: '${self:provider.stage}' },
|
|
@@ -371,8 +461,13 @@ const createVPCInfrastructure = (AppDefinition) => {
|
|
|
371
461
|
VpcId: { Ref: 'FriggVPC' },
|
|
372
462
|
ServiceName: 'com.amazonaws.${self:provider.region}.kms',
|
|
373
463
|
VpcEndpointType: 'Interface',
|
|
374
|
-
SubnetIds: [
|
|
375
|
-
|
|
464
|
+
SubnetIds: [
|
|
465
|
+
{ Ref: 'FriggPrivateSubnet1' },
|
|
466
|
+
{ Ref: 'FriggPrivateSubnet2' },
|
|
467
|
+
],
|
|
468
|
+
SecurityGroupIds: [
|
|
469
|
+
{ Ref: 'FriggVPCEndpointSecurityGroup' },
|
|
470
|
+
],
|
|
376
471
|
PrivateDnsEnabled: true,
|
|
377
472
|
},
|
|
378
473
|
};
|
|
@@ -382,9 +477,13 @@ const createVPCInfrastructure = (AppDefinition) => {
|
|
|
382
477
|
Type: 'AWS::EC2::VPCEndpoint',
|
|
383
478
|
Properties: {
|
|
384
479
|
VpcId: { Ref: 'FriggVPC' },
|
|
385
|
-
ServiceName:
|
|
480
|
+
ServiceName:
|
|
481
|
+
'com.amazonaws.${self:provider.region}.secretsmanager',
|
|
386
482
|
VpcEndpointType: 'Interface',
|
|
387
|
-
SubnetIds: [
|
|
483
|
+
SubnetIds: [
|
|
484
|
+
{ Ref: 'FriggPrivateSubnet1' },
|
|
485
|
+
{ Ref: 'FriggPrivateSubnet2' },
|
|
486
|
+
],
|
|
388
487
|
SecurityGroupIds: [{ Ref: 'FriggVPCEndpointSecurityGroup' }],
|
|
389
488
|
PrivateDnsEnabled: true,
|
|
390
489
|
},
|
|
@@ -393,14 +492,17 @@ const createVPCInfrastructure = (AppDefinition) => {
|
|
|
393
492
|
vpcResources.FriggVPCEndpointSecurityGroup = {
|
|
394
493
|
Type: 'AWS::EC2::SecurityGroup',
|
|
395
494
|
Properties: {
|
|
396
|
-
GroupDescription:
|
|
495
|
+
GroupDescription:
|
|
496
|
+
'Security group for Frigg VPC Endpoints - allows HTTPS from Lambda functions',
|
|
397
497
|
VpcId: { Ref: 'FriggVPC' },
|
|
398
498
|
SecurityGroupIngress: [
|
|
399
499
|
{
|
|
400
500
|
IpProtocol: 'tcp',
|
|
401
501
|
FromPort: 443,
|
|
402
502
|
ToPort: 443,
|
|
403
|
-
SourceSecurityGroupId: {
|
|
503
|
+
SourceSecurityGroupId: {
|
|
504
|
+
Ref: 'FriggLambdaSecurityGroup',
|
|
505
|
+
},
|
|
404
506
|
Description: 'HTTPS from Lambda security group',
|
|
405
507
|
},
|
|
406
508
|
{
|
|
@@ -412,12 +514,18 @@ const createVPCInfrastructure = (AppDefinition) => {
|
|
|
412
514
|
},
|
|
413
515
|
],
|
|
414
516
|
Tags: [
|
|
415
|
-
{
|
|
517
|
+
{
|
|
518
|
+
Key: 'Name',
|
|
519
|
+
Value: '${self:service}-${self:provider.stage}-vpc-endpoint-sg',
|
|
520
|
+
},
|
|
416
521
|
{ Key: 'ManagedBy', Value: 'Frigg' },
|
|
417
522
|
{ Key: 'Service', Value: '${self:service}' },
|
|
418
523
|
{ Key: 'Stage', Value: '${self:provider.stage}' },
|
|
419
524
|
{ Key: 'Type', Value: 'VPCEndpoint' },
|
|
420
|
-
{
|
|
525
|
+
{
|
|
526
|
+
Key: 'Purpose',
|
|
527
|
+
Value: 'Allow Lambda functions to access VPC endpoints',
|
|
528
|
+
},
|
|
421
529
|
],
|
|
422
530
|
},
|
|
423
531
|
};
|
|
@@ -447,13 +555,18 @@ const gatherDiscoveredResources = async (AppDefinition) => {
|
|
|
447
555
|
if (discoveredResources.defaultVpcId) {
|
|
448
556
|
console.log(` VPC: ${discoveredResources.defaultVpcId}`);
|
|
449
557
|
}
|
|
450
|
-
if (
|
|
558
|
+
if (
|
|
559
|
+
discoveredResources.privateSubnetId1 &&
|
|
560
|
+
discoveredResources.privateSubnetId2
|
|
561
|
+
) {
|
|
451
562
|
console.log(
|
|
452
563
|
` Subnets: ${discoveredResources.privateSubnetId1}, ${discoveredResources.privateSubnetId2}`
|
|
453
564
|
);
|
|
454
565
|
}
|
|
455
566
|
if (discoveredResources.defaultSecurityGroupId) {
|
|
456
|
-
console.log(
|
|
567
|
+
console.log(
|
|
568
|
+
` Security Group: ${discoveredResources.defaultSecurityGroupId}`
|
|
569
|
+
);
|
|
457
570
|
}
|
|
458
571
|
if (discoveredResources.defaultKmsKeyId) {
|
|
459
572
|
console.log(` KMS Key: ${discoveredResources.defaultKmsKeyId}`);
|
|
@@ -492,7 +605,11 @@ const buildEnvironment = (appEnvironmentVars, discoveredResources) => {
|
|
|
492
605
|
return environment;
|
|
493
606
|
};
|
|
494
607
|
|
|
495
|
-
const createBaseDefinition = (
|
|
608
|
+
const createBaseDefinition = (
|
|
609
|
+
AppDefinition,
|
|
610
|
+
appEnvironmentVars,
|
|
611
|
+
discoveredResources
|
|
612
|
+
) => {
|
|
496
613
|
const region = process.env.AWS_REGION || 'us-east-1';
|
|
497
614
|
|
|
498
615
|
return {
|
|
@@ -500,7 +617,11 @@ const createBaseDefinition = (AppDefinition, appEnvironmentVars, discoveredResou
|
|
|
500
617
|
service: AppDefinition.name || 'create-frigg-app',
|
|
501
618
|
package: {
|
|
502
619
|
individually: true,
|
|
503
|
-
exclude: [
|
|
620
|
+
exclude: [
|
|
621
|
+
'!**/node_modules/aws-sdk/**',
|
|
622
|
+
'!**/node_modules/@aws-sdk/**',
|
|
623
|
+
'!package.json',
|
|
624
|
+
],
|
|
504
625
|
},
|
|
505
626
|
useDotenv: true,
|
|
506
627
|
provider: {
|
|
@@ -509,7 +630,10 @@ const createBaseDefinition = (AppDefinition, appEnvironmentVars, discoveredResou
|
|
|
509
630
|
timeout: 30,
|
|
510
631
|
region,
|
|
511
632
|
stage: '${opt:stage}',
|
|
512
|
-
environment: buildEnvironment(
|
|
633
|
+
environment: buildEnvironment(
|
|
634
|
+
appEnvironmentVars,
|
|
635
|
+
discoveredResources
|
|
636
|
+
),
|
|
513
637
|
iamRoleStatements: [
|
|
514
638
|
{
|
|
515
639
|
Effect: 'Allow',
|
|
@@ -518,13 +642,20 @@ const createBaseDefinition = (AppDefinition, appEnvironmentVars, discoveredResou
|
|
|
518
642
|
},
|
|
519
643
|
{
|
|
520
644
|
Effect: 'Allow',
|
|
521
|
-
Action: [
|
|
645
|
+
Action: [
|
|
646
|
+
'sqs:SendMessage',
|
|
647
|
+
'sqs:SendMessageBatch',
|
|
648
|
+
'sqs:GetQueueUrl',
|
|
649
|
+
'sqs:GetQueueAttributes',
|
|
650
|
+
],
|
|
522
651
|
Resource: [
|
|
523
652
|
{ 'Fn::GetAtt': ['InternalErrorQueue', 'Arn'] },
|
|
524
653
|
{
|
|
525
654
|
'Fn::Join': [
|
|
526
655
|
':',
|
|
527
|
-
[
|
|
656
|
+
[
|
|
657
|
+
'arn:aws:sqs:${self:provider.region}:*:${self:service}--${self:provider.stage}-*Queue',
|
|
658
|
+
],
|
|
528
659
|
],
|
|
529
660
|
},
|
|
530
661
|
],
|
|
@@ -543,7 +674,8 @@ const createBaseDefinition = (AppDefinition, appEnvironmentVars, discoveredResou
|
|
|
543
674
|
},
|
|
544
675
|
},
|
|
545
676
|
plugins: [
|
|
546
|
-
|
|
677
|
+
// Temporarily disabled Jetpack - it ignores package.patterns in dependency mode
|
|
678
|
+
// 'serverless-jetpack',
|
|
547
679
|
'serverless-dotenv-plugin',
|
|
548
680
|
'serverless-offline-sqs',
|
|
549
681
|
'serverless-offline',
|
|
@@ -564,25 +696,36 @@ const createBaseDefinition = (AppDefinition, appEnvironmentVars, discoveredResou
|
|
|
564
696
|
secretAccessKey: 'root',
|
|
565
697
|
skipCacheInvalidation: false,
|
|
566
698
|
},
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
699
|
+
// Jetpack config removed - testing with standard Serverless packaging
|
|
700
|
+
// jetpack: {
|
|
701
|
+
// base: '..',
|
|
702
|
+
// },
|
|
570
703
|
},
|
|
571
704
|
functions: {
|
|
572
705
|
auth: {
|
|
573
|
-
handler:
|
|
706
|
+
handler:
|
|
707
|
+
'node_modules/@friggframework/core/handlers/routers/auth.handler',
|
|
574
708
|
events: [
|
|
575
709
|
{ httpApi: { path: '/api/integrations', method: 'ANY' } },
|
|
576
|
-
{
|
|
710
|
+
{
|
|
711
|
+
httpApi: {
|
|
712
|
+
path: '/api/integrations/{proxy+}',
|
|
713
|
+
method: 'ANY',
|
|
714
|
+
},
|
|
715
|
+
},
|
|
577
716
|
{ httpApi: { path: '/api/authorize', method: 'ANY' } },
|
|
578
717
|
],
|
|
579
718
|
},
|
|
580
719
|
user: {
|
|
581
|
-
handler:
|
|
582
|
-
|
|
720
|
+
handler:
|
|
721
|
+
'node_modules/@friggframework/core/handlers/routers/user.handler',
|
|
722
|
+
events: [
|
|
723
|
+
{ httpApi: { path: '/user/{proxy+}', method: 'ANY' } },
|
|
724
|
+
],
|
|
583
725
|
},
|
|
584
726
|
health: {
|
|
585
|
-
handler:
|
|
727
|
+
handler:
|
|
728
|
+
'node_modules/@friggframework/core/handlers/routers/health.handler',
|
|
586
729
|
events: [
|
|
587
730
|
{ httpApi: { path: '/health', method: 'GET' } },
|
|
588
731
|
{ httpApi: { path: '/health/{proxy+}', method: 'GET' } },
|
|
@@ -594,7 +737,8 @@ const createBaseDefinition = (AppDefinition, appEnvironmentVars, discoveredResou
|
|
|
594
737
|
InternalErrorQueue: {
|
|
595
738
|
Type: 'AWS::SQS::Queue',
|
|
596
739
|
Properties: {
|
|
597
|
-
QueueName:
|
|
740
|
+
QueueName:
|
|
741
|
+
'${self:service}-internal-error-queue-${self:provider.stage}',
|
|
598
742
|
MessageRetentionPeriod: 300,
|
|
599
743
|
},
|
|
600
744
|
},
|
|
@@ -604,7 +748,9 @@ const createBaseDefinition = (AppDefinition, appEnvironmentVars, discoveredResou
|
|
|
604
748
|
Subscription: [
|
|
605
749
|
{
|
|
606
750
|
Protocol: 'sqs',
|
|
607
|
-
Endpoint: {
|
|
751
|
+
Endpoint: {
|
|
752
|
+
'Fn::GetAtt': ['InternalErrorQueue', 'Arn'],
|
|
753
|
+
},
|
|
608
754
|
},
|
|
609
755
|
],
|
|
610
756
|
},
|
|
@@ -620,10 +766,22 @@ const createBaseDefinition = (AppDefinition, appEnvironmentVars, discoveredResou
|
|
|
620
766
|
Sid: 'Allow Dead Letter SNS to publish to SQS',
|
|
621
767
|
Effect: 'Allow',
|
|
622
768
|
Principal: { Service: 'sns.amazonaws.com' },
|
|
623
|
-
Resource: {
|
|
624
|
-
|
|
769
|
+
Resource: {
|
|
770
|
+
'Fn::GetAtt': [
|
|
771
|
+
'InternalErrorQueue',
|
|
772
|
+
'Arn',
|
|
773
|
+
],
|
|
774
|
+
},
|
|
775
|
+
Action: [
|
|
776
|
+
'SQS:SendMessage',
|
|
777
|
+
'SQS:SendMessageBatch',
|
|
778
|
+
],
|
|
625
779
|
Condition: {
|
|
626
|
-
ArnEquals: {
|
|
780
|
+
ArnEquals: {
|
|
781
|
+
'aws:SourceArn': {
|
|
782
|
+
Ref: 'InternalErrorBridgeTopic',
|
|
783
|
+
},
|
|
784
|
+
},
|
|
627
785
|
},
|
|
628
786
|
},
|
|
629
787
|
],
|
|
@@ -653,24 +811,33 @@ const createBaseDefinition = (AppDefinition, appEnvironmentVars, discoveredResou
|
|
|
653
811
|
};
|
|
654
812
|
};
|
|
655
813
|
|
|
656
|
-
const applyKmsConfiguration = (
|
|
814
|
+
const applyKmsConfiguration = (
|
|
815
|
+
definition,
|
|
816
|
+
AppDefinition,
|
|
817
|
+
discoveredResources
|
|
818
|
+
) => {
|
|
657
819
|
if (AppDefinition.encryption?.fieldLevelEncryptionMethod !== 'kms') {
|
|
658
820
|
return;
|
|
659
821
|
}
|
|
660
822
|
|
|
661
823
|
// Skip KMS configuration for local development when AWS discovery is disabled
|
|
662
824
|
if (process.env.FRIGG_SKIP_AWS_DISCOVERY === 'true') {
|
|
663
|
-
console.log(
|
|
825
|
+
console.log(
|
|
826
|
+
'⚙️ Skipping KMS configuration for local development (FRIGG_SKIP_AWS_DISCOVERY is set)'
|
|
827
|
+
);
|
|
664
828
|
return;
|
|
665
829
|
}
|
|
666
830
|
|
|
667
831
|
if (discoveredResources.defaultKmsKeyId) {
|
|
668
|
-
console.log(
|
|
832
|
+
console.log(
|
|
833
|
+
`Using existing KMS key: ${discoveredResources.defaultKmsKeyId}`
|
|
834
|
+
);
|
|
669
835
|
definition.resources.Resources.FriggKMSKeyAlias = {
|
|
670
836
|
Type: 'AWS::KMS::Alias',
|
|
671
837
|
DeletionPolicy: 'Retain',
|
|
672
838
|
Properties: {
|
|
673
|
-
AliasName:
|
|
839
|
+
AliasName:
|
|
840
|
+
'alias/${self:service}-${self:provider.stage}-frigg-kms',
|
|
674
841
|
TargetKeyId: discoveredResources.defaultKmsKeyId,
|
|
675
842
|
},
|
|
676
843
|
};
|
|
@@ -684,7 +851,7 @@ const applyKmsConfiguration = (definition, AppDefinition, discoveredResources) =
|
|
|
684
851
|
if (AppDefinition.encryption?.createResourceIfNoneFound !== true) {
|
|
685
852
|
throw new Error(
|
|
686
853
|
'KMS field-level encryption is enabled but no KMS key was found. ' +
|
|
687
|
-
|
|
854
|
+
'Either provide an existing KMS key or set encryption.createResourceIfNoneFound to true to create a new key.'
|
|
688
855
|
);
|
|
689
856
|
}
|
|
690
857
|
|
|
@@ -703,7 +870,10 @@ const applyKmsConfiguration = (definition, AppDefinition, discoveredResources) =
|
|
|
703
870
|
Sid: 'AllowRootAccountAdmin',
|
|
704
871
|
Effect: 'Allow',
|
|
705
872
|
Principal: {
|
|
706
|
-
AWS: {
|
|
873
|
+
AWS: {
|
|
874
|
+
'Fn::Sub':
|
|
875
|
+
'arn:aws:iam::${AWS::AccountId}:root',
|
|
876
|
+
},
|
|
707
877
|
},
|
|
708
878
|
Action: 'kms:*',
|
|
709
879
|
Resource: '*',
|
|
@@ -712,20 +882,31 @@ const applyKmsConfiguration = (definition, AppDefinition, discoveredResources) =
|
|
|
712
882
|
Sid: 'AllowLambdaService',
|
|
713
883
|
Effect: 'Allow',
|
|
714
884
|
Principal: { Service: 'lambda.amazonaws.com' },
|
|
715
|
-
Action: [
|
|
885
|
+
Action: [
|
|
886
|
+
'kms:GenerateDataKey',
|
|
887
|
+
'kms:Decrypt',
|
|
888
|
+
'kms:DescribeKey',
|
|
889
|
+
],
|
|
716
890
|
Resource: '*',
|
|
717
891
|
Condition: {
|
|
718
892
|
StringEquals: {
|
|
719
|
-
'kms:ViaService': `lambda.${process.env.AWS_REGION || 'us-east-1'
|
|
893
|
+
'kms:ViaService': `lambda.${process.env.AWS_REGION || 'us-east-1'
|
|
894
|
+
}.amazonaws.com`,
|
|
720
895
|
},
|
|
721
896
|
},
|
|
722
897
|
},
|
|
723
898
|
],
|
|
724
899
|
},
|
|
725
900
|
Tags: [
|
|
726
|
-
{
|
|
901
|
+
{
|
|
902
|
+
Key: 'Name',
|
|
903
|
+
Value: '${self:service}-${self:provider.stage}-frigg-kms-key',
|
|
904
|
+
},
|
|
727
905
|
{ Key: 'ManagedBy', Value: 'Frigg' },
|
|
728
|
-
{
|
|
906
|
+
{
|
|
907
|
+
Key: 'Purpose',
|
|
908
|
+
Value: 'Field-level encryption for Frigg application',
|
|
909
|
+
},
|
|
729
910
|
],
|
|
730
911
|
},
|
|
731
912
|
};
|
|
@@ -734,7 +915,8 @@ const applyKmsConfiguration = (definition, AppDefinition, discoveredResources) =
|
|
|
734
915
|
Type: 'AWS::KMS::Alias',
|
|
735
916
|
DeletionPolicy: 'Retain',
|
|
736
917
|
Properties: {
|
|
737
|
-
AliasName:
|
|
918
|
+
AliasName:
|
|
919
|
+
'alias/${self:service}-${self:provider.stage}-frigg-kms',
|
|
738
920
|
TargetKeyId: { 'Fn::GetAtt': ['FriggKMSKey', 'Arn'] },
|
|
739
921
|
},
|
|
740
922
|
};
|
|
@@ -745,7 +927,9 @@ const applyKmsConfiguration = (definition, AppDefinition, discoveredResources) =
|
|
|
745
927
|
Resource: [{ 'Fn::GetAtt': ['FriggKMSKey', 'Arn'] }],
|
|
746
928
|
});
|
|
747
929
|
|
|
748
|
-
definition.provider.environment.KMS_KEY_ARN = {
|
|
930
|
+
definition.provider.environment.KMS_KEY_ARN = {
|
|
931
|
+
'Fn::GetAtt': ['FriggKMSKey', 'Arn'],
|
|
932
|
+
};
|
|
749
933
|
definition.custom.kmsGrants = {
|
|
750
934
|
kmsKeyId: { 'Fn::GetAtt': ['FriggKMSKey', 'Arn'] },
|
|
751
935
|
};
|
|
@@ -754,13 +938,16 @@ const applyKmsConfiguration = (definition, AppDefinition, discoveredResources) =
|
|
|
754
938
|
definition.plugins.push('serverless-kms-grants');
|
|
755
939
|
if (!definition.custom.kmsGrants) {
|
|
756
940
|
definition.custom.kmsGrants = {
|
|
757
|
-
kmsKeyId:
|
|
941
|
+
kmsKeyId:
|
|
942
|
+
discoveredResources.defaultKmsKeyId ||
|
|
943
|
+
'${env:AWS_DISCOVERY_KMS_KEY_ID}',
|
|
758
944
|
};
|
|
759
945
|
}
|
|
760
946
|
|
|
761
947
|
if (!definition.provider.environment.KMS_KEY_ARN) {
|
|
762
948
|
definition.provider.environment.KMS_KEY_ARN =
|
|
763
|
-
discoveredResources.defaultKmsKeyId ||
|
|
949
|
+
discoveredResources.defaultKmsKeyId ||
|
|
950
|
+
'${env:AWS_DISCOVERY_KMS_KEY_ID}';
|
|
764
951
|
}
|
|
765
952
|
};
|
|
766
953
|
|
|
@@ -777,7 +964,9 @@ const healVpcConfiguration = (discoveredResources, AppDefinition) => {
|
|
|
777
964
|
return healingReport;
|
|
778
965
|
}
|
|
779
966
|
|
|
780
|
-
console.log(
|
|
967
|
+
console.log(
|
|
968
|
+
'🔧 Self-healing mode enabled - checking for VPC misconfigurations...'
|
|
969
|
+
);
|
|
781
970
|
|
|
782
971
|
if (discoveredResources.natGatewayInPrivateSubnet) {
|
|
783
972
|
healingReport.warnings.push(
|
|
@@ -787,7 +976,9 @@ const healVpcConfiguration = (discoveredResources, AppDefinition) => {
|
|
|
787
976
|
'NAT Gateway should be recreated in a public subnet for proper internet connectivity'
|
|
788
977
|
);
|
|
789
978
|
discoveredResources.needsNewNatGateway = true;
|
|
790
|
-
healingReport.healed.push(
|
|
979
|
+
healingReport.healed.push(
|
|
980
|
+
'Marked NAT Gateway for recreation in public subnet'
|
|
981
|
+
);
|
|
791
982
|
}
|
|
792
983
|
|
|
793
984
|
if (discoveredResources.elasticIpAlreadyAssociated) {
|
|
@@ -796,10 +987,14 @@ const healVpcConfiguration = (discoveredResources, AppDefinition) => {
|
|
|
796
987
|
);
|
|
797
988
|
|
|
798
989
|
if (discoveredResources.existingNatGatewayId) {
|
|
799
|
-
healingReport.healed.push(
|
|
990
|
+
healingReport.healed.push(
|
|
991
|
+
'Will reuse existing NAT Gateway instead of creating a new one'
|
|
992
|
+
);
|
|
800
993
|
discoveredResources.reuseExistingNatGateway = true;
|
|
801
994
|
} else {
|
|
802
|
-
healingReport.healed.push(
|
|
995
|
+
healingReport.healed.push(
|
|
996
|
+
'Will allocate a new Elastic IP for NAT Gateway'
|
|
997
|
+
);
|
|
803
998
|
discoveredResources.allocateNewElasticIp = true;
|
|
804
999
|
}
|
|
805
1000
|
}
|
|
@@ -823,19 +1018,25 @@ const healVpcConfiguration = (discoveredResources, AppDefinition) => {
|
|
|
823
1018
|
healingReport.warnings.push(
|
|
824
1019
|
'Subnet configuration mismatch detected - Lambda functions require private subnets'
|
|
825
1020
|
);
|
|
826
|
-
healingReport.healed.push(
|
|
1021
|
+
healingReport.healed.push(
|
|
1022
|
+
'Will create proper route table configuration for subnet isolation'
|
|
1023
|
+
);
|
|
827
1024
|
}
|
|
828
1025
|
|
|
829
1026
|
if (discoveredResources.orphanedElasticIps?.length > 0) {
|
|
830
1027
|
healingReport.warnings.push(
|
|
831
1028
|
`Found ${discoveredResources.orphanedElasticIps.length} orphaned Elastic IPs`
|
|
832
1029
|
);
|
|
833
|
-
healingReport.recommendations.push(
|
|
1030
|
+
healingReport.recommendations.push(
|
|
1031
|
+
'Consider releasing orphaned Elastic IPs to avoid charges'
|
|
1032
|
+
);
|
|
834
1033
|
}
|
|
835
1034
|
|
|
836
1035
|
if (healingReport.criticalActions.length > 0) {
|
|
837
1036
|
console.log('🚨 CRITICAL ACTIONS:');
|
|
838
|
-
healingReport.criticalActions.forEach((action) =>
|
|
1037
|
+
healingReport.criticalActions.forEach((action) =>
|
|
1038
|
+
console.log(` - ${action}`)
|
|
1039
|
+
);
|
|
839
1040
|
}
|
|
840
1041
|
|
|
841
1042
|
if (healingReport.healed.length > 0) {
|
|
@@ -845,12 +1046,16 @@ const healVpcConfiguration = (discoveredResources, AppDefinition) => {
|
|
|
845
1046
|
|
|
846
1047
|
if (healingReport.warnings.length > 0) {
|
|
847
1048
|
console.log('⚠️ Issues detected:');
|
|
848
|
-
healingReport.warnings.forEach((warning) =>
|
|
1049
|
+
healingReport.warnings.forEach((warning) =>
|
|
1050
|
+
console.log(` - ${warning}`)
|
|
1051
|
+
);
|
|
849
1052
|
}
|
|
850
1053
|
|
|
851
1054
|
if (healingReport.recommendations.length > 0) {
|
|
852
1055
|
console.log('💡 Recommendations:');
|
|
853
|
-
healingReport.recommendations.forEach((rec) =>
|
|
1056
|
+
healingReport.recommendations.forEach((rec) =>
|
|
1057
|
+
console.log(` - ${rec}`)
|
|
1058
|
+
);
|
|
854
1059
|
}
|
|
855
1060
|
|
|
856
1061
|
return healingReport;
|
|
@@ -863,7 +1068,9 @@ const configureVpc = (definition, AppDefinition, discoveredResources) => {
|
|
|
863
1068
|
|
|
864
1069
|
// Skip VPC configuration for local development when AWS discovery is disabled
|
|
865
1070
|
if (process.env.FRIGG_SKIP_AWS_DISCOVERY === 'true') {
|
|
866
|
-
console.log(
|
|
1071
|
+
console.log(
|
|
1072
|
+
'⚙️ Skipping VPC configuration for local development (FRIGG_SKIP_AWS_DISCOVERY is set)'
|
|
1073
|
+
);
|
|
867
1074
|
return;
|
|
868
1075
|
}
|
|
869
1076
|
|
|
@@ -880,9 +1087,16 @@ const configureVpc = (definition, AppDefinition, discoveredResources) => {
|
|
|
880
1087
|
});
|
|
881
1088
|
|
|
882
1089
|
if (Object.keys(discoveredResources).length > 0) {
|
|
883
|
-
const healingReport = healVpcConfiguration(
|
|
1090
|
+
const healingReport = healVpcConfiguration(
|
|
1091
|
+
discoveredResources,
|
|
1092
|
+
AppDefinition
|
|
1093
|
+
);
|
|
884
1094
|
if (healingReport.errors.length > 0 && !AppDefinition.vpc?.selfHeal) {
|
|
885
|
-
throw new Error(
|
|
1095
|
+
throw new Error(
|
|
1096
|
+
`VPC configuration errors detected: ${healingReport.errors.join(
|
|
1097
|
+
', '
|
|
1098
|
+
)}`
|
|
1099
|
+
);
|
|
886
1100
|
}
|
|
887
1101
|
}
|
|
888
1102
|
|
|
@@ -899,15 +1113,21 @@ const configureVpc = (definition, AppDefinition, discoveredResources) => {
|
|
|
899
1113
|
const vpcResources = createVPCInfrastructure(AppDefinition);
|
|
900
1114
|
Object.assign(definition.resources.Resources, vpcResources);
|
|
901
1115
|
vpcId = { Ref: 'FriggVPC' };
|
|
902
|
-
vpcConfig.securityGroupIds = AppDefinition.vpc.securityGroupIds || [
|
|
1116
|
+
vpcConfig.securityGroupIds = AppDefinition.vpc.securityGroupIds || [
|
|
1117
|
+
{ Ref: 'FriggLambdaSecurityGroup' },
|
|
1118
|
+
];
|
|
903
1119
|
} else if (vpcManagement === 'use-existing') {
|
|
904
1120
|
if (!AppDefinition.vpc.vpcId) {
|
|
905
|
-
throw new Error(
|
|
1121
|
+
throw new Error(
|
|
1122
|
+
'VPC management is set to "use-existing" but no vpcId was provided'
|
|
1123
|
+
);
|
|
906
1124
|
}
|
|
907
1125
|
vpcId = AppDefinition.vpc.vpcId;
|
|
908
1126
|
vpcConfig.securityGroupIds =
|
|
909
1127
|
AppDefinition.vpc.securityGroupIds ||
|
|
910
|
-
(discoveredResources.defaultSecurityGroupId
|
|
1128
|
+
(discoveredResources.defaultSecurityGroupId
|
|
1129
|
+
? [discoveredResources.defaultSecurityGroupId]
|
|
1130
|
+
: []);
|
|
911
1131
|
} else {
|
|
912
1132
|
if (!discoveredResources.defaultVpcId) {
|
|
913
1133
|
throw new Error(
|
|
@@ -917,11 +1137,15 @@ const configureVpc = (definition, AppDefinition, discoveredResources) => {
|
|
|
917
1137
|
vpcId = discoveredResources.defaultVpcId;
|
|
918
1138
|
vpcConfig.securityGroupIds =
|
|
919
1139
|
AppDefinition.vpc.securityGroupIds ||
|
|
920
|
-
(discoveredResources.defaultSecurityGroupId
|
|
1140
|
+
(discoveredResources.defaultSecurityGroupId
|
|
1141
|
+
? [discoveredResources.defaultSecurityGroupId]
|
|
1142
|
+
: []);
|
|
921
1143
|
}
|
|
922
1144
|
|
|
923
|
-
const defaultSubnetManagement =
|
|
924
|
-
|
|
1145
|
+
const defaultSubnetManagement =
|
|
1146
|
+
vpcManagement === 'create-new' ? 'create' : 'discover';
|
|
1147
|
+
let subnetManagement =
|
|
1148
|
+
AppDefinition.vpc.subnets?.management || defaultSubnetManagement;
|
|
925
1149
|
console.log(`Subnet Management Mode: ${subnetManagement}`);
|
|
926
1150
|
|
|
927
1151
|
const effectiveVpcId = vpcId || discoveredResources.defaultVpcId;
|
|
@@ -931,7 +1155,10 @@ const configureVpc = (definition, AppDefinition, discoveredResources) => {
|
|
|
931
1155
|
|
|
932
1156
|
if (subnetManagement === 'create') {
|
|
933
1157
|
console.log('Creating new subnets...');
|
|
934
|
-
const subnetVpcId =
|
|
1158
|
+
const subnetVpcId =
|
|
1159
|
+
vpcManagement === 'create-new'
|
|
1160
|
+
? { Ref: 'FriggVPC' }
|
|
1161
|
+
: effectiveVpcId;
|
|
935
1162
|
let subnet1Cidr;
|
|
936
1163
|
let subnet2Cidr;
|
|
937
1164
|
let publicSubnetCidr;
|
|
@@ -954,7 +1181,10 @@ const configureVpc = (definition, AppDefinition, discoveredResources) => {
|
|
|
954
1181
|
CidrBlock: subnet1Cidr,
|
|
955
1182
|
AvailabilityZone: { 'Fn::Select': [0, { 'Fn::GetAZs': '' }] },
|
|
956
1183
|
Tags: [
|
|
957
|
-
{
|
|
1184
|
+
{
|
|
1185
|
+
Key: 'Name',
|
|
1186
|
+
Value: '${self:service}-${self:provider.stage}-private-1',
|
|
1187
|
+
},
|
|
958
1188
|
{ Key: 'Type', Value: 'Private' },
|
|
959
1189
|
{ Key: 'ManagedBy', Value: 'Frigg' },
|
|
960
1190
|
],
|
|
@@ -968,7 +1198,10 @@ const configureVpc = (definition, AppDefinition, discoveredResources) => {
|
|
|
968
1198
|
CidrBlock: subnet2Cidr,
|
|
969
1199
|
AvailabilityZone: { 'Fn::Select': [1, { 'Fn::GetAZs': '' }] },
|
|
970
1200
|
Tags: [
|
|
971
|
-
{
|
|
1201
|
+
{
|
|
1202
|
+
Key: 'Name',
|
|
1203
|
+
Value: '${self:service}-${self:provider.stage}-private-2',
|
|
1204
|
+
},
|
|
972
1205
|
{ Key: 'Type', Value: 'Private' },
|
|
973
1206
|
{ Key: 'ManagedBy', Value: 'Frigg' },
|
|
974
1207
|
],
|
|
@@ -983,23 +1216,38 @@ const configureVpc = (definition, AppDefinition, discoveredResources) => {
|
|
|
983
1216
|
MapPublicIpOnLaunch: true,
|
|
984
1217
|
AvailabilityZone: { 'Fn::Select': [0, { 'Fn::GetAZs': '' }] },
|
|
985
1218
|
Tags: [
|
|
986
|
-
{
|
|
1219
|
+
{
|
|
1220
|
+
Key: 'Name',
|
|
1221
|
+
Value: '${self:service}-${self:provider.stage}-public',
|
|
1222
|
+
},
|
|
987
1223
|
{ Key: 'Type', Value: 'Public' },
|
|
988
1224
|
{ Key: 'ManagedBy', Value: 'Frigg' },
|
|
989
1225
|
],
|
|
990
1226
|
},
|
|
991
1227
|
};
|
|
992
1228
|
|
|
993
|
-
vpcConfig.subnetIds = [
|
|
1229
|
+
vpcConfig.subnetIds = [
|
|
1230
|
+
{ Ref: 'FriggPrivateSubnet1' },
|
|
1231
|
+
{ Ref: 'FriggPrivateSubnet2' },
|
|
1232
|
+
];
|
|
994
1233
|
|
|
995
|
-
if (
|
|
996
|
-
|
|
1234
|
+
if (
|
|
1235
|
+
!AppDefinition.vpc.natGateway ||
|
|
1236
|
+
AppDefinition.vpc.natGateway.management === 'discover'
|
|
1237
|
+
) {
|
|
1238
|
+
if (
|
|
1239
|
+
vpcManagement === 'create-new' ||
|
|
1240
|
+
!discoveredResources.internetGatewayId
|
|
1241
|
+
) {
|
|
997
1242
|
if (!definition.resources.Resources.FriggInternetGateway) {
|
|
998
1243
|
definition.resources.Resources.FriggInternetGateway = {
|
|
999
1244
|
Type: 'AWS::EC2::InternetGateway',
|
|
1000
1245
|
Properties: {
|
|
1001
1246
|
Tags: [
|
|
1002
|
-
{
|
|
1247
|
+
{
|
|
1248
|
+
Key: 'Name',
|
|
1249
|
+
Value: '${self:service}-${self:provider.stage}-igw',
|
|
1250
|
+
},
|
|
1003
1251
|
{ Key: 'ManagedBy', Value: 'Frigg' },
|
|
1004
1252
|
],
|
|
1005
1253
|
},
|
|
@@ -1020,7 +1268,10 @@ const configureVpc = (definition, AppDefinition, discoveredResources) => {
|
|
|
1020
1268
|
Properties: {
|
|
1021
1269
|
VpcId: subnetVpcId,
|
|
1022
1270
|
Tags: [
|
|
1023
|
-
{
|
|
1271
|
+
{
|
|
1272
|
+
Key: 'Name',
|
|
1273
|
+
Value: '${self:service}-${self:provider.stage}-public-rt',
|
|
1274
|
+
},
|
|
1024
1275
|
{ Key: 'ManagedBy', Value: 'Frigg' },
|
|
1025
1276
|
],
|
|
1026
1277
|
},
|
|
@@ -1028,15 +1279,21 @@ const configureVpc = (definition, AppDefinition, discoveredResources) => {
|
|
|
1028
1279
|
|
|
1029
1280
|
definition.resources.Resources.FriggPublicRoute = {
|
|
1030
1281
|
Type: 'AWS::EC2::Route',
|
|
1031
|
-
DependsOn:
|
|
1282
|
+
DependsOn:
|
|
1283
|
+
vpcManagement === 'create-new'
|
|
1284
|
+
? 'FriggIGWAttachment'
|
|
1285
|
+
: undefined,
|
|
1032
1286
|
Properties: {
|
|
1033
1287
|
RouteTableId: { Ref: 'FriggPublicRouteTable' },
|
|
1034
1288
|
DestinationCidrBlock: '0.0.0.0/0',
|
|
1035
|
-
GatewayId: discoveredResources.internetGatewayId || {
|
|
1289
|
+
GatewayId: discoveredResources.internetGatewayId || {
|
|
1290
|
+
Ref: 'FriggInternetGateway',
|
|
1291
|
+
},
|
|
1036
1292
|
},
|
|
1037
1293
|
};
|
|
1038
1294
|
|
|
1039
|
-
definition.resources.Resources.FriggPublicSubnetRouteTableAssociation =
|
|
1295
|
+
definition.resources.Resources.FriggPublicSubnetRouteTableAssociation =
|
|
1296
|
+
{
|
|
1040
1297
|
Type: 'AWS::EC2::SubnetRouteTableAssociation',
|
|
1041
1298
|
Properties: {
|
|
1042
1299
|
SubnetId: { Ref: 'FriggPublicSubnet' },
|
|
@@ -1049,13 +1306,17 @@ const configureVpc = (definition, AppDefinition, discoveredResources) => {
|
|
|
1049
1306
|
Properties: {
|
|
1050
1307
|
VpcId: subnetVpcId,
|
|
1051
1308
|
Tags: [
|
|
1052
|
-
{
|
|
1309
|
+
{
|
|
1310
|
+
Key: 'Name',
|
|
1311
|
+
Value: '${self:service}-${self:provider.stage}-lambda-rt',
|
|
1312
|
+
},
|
|
1053
1313
|
{ Key: 'ManagedBy', Value: 'Frigg' },
|
|
1054
1314
|
],
|
|
1055
1315
|
},
|
|
1056
1316
|
};
|
|
1057
1317
|
|
|
1058
|
-
definition.resources.Resources.FriggPrivateSubnet1RouteTableAssociation =
|
|
1318
|
+
definition.resources.Resources.FriggPrivateSubnet1RouteTableAssociation =
|
|
1319
|
+
{
|
|
1059
1320
|
Type: 'AWS::EC2::SubnetRouteTableAssociation',
|
|
1060
1321
|
Properties: {
|
|
1061
1322
|
SubnetId: { Ref: 'FriggPrivateSubnet1' },
|
|
@@ -1063,7 +1324,8 @@ const configureVpc = (definition, AppDefinition, discoveredResources) => {
|
|
|
1063
1324
|
},
|
|
1064
1325
|
};
|
|
1065
1326
|
|
|
1066
|
-
definition.resources.Resources.FriggPrivateSubnet2RouteTableAssociation =
|
|
1327
|
+
definition.resources.Resources.FriggPrivateSubnet2RouteTableAssociation =
|
|
1328
|
+
{
|
|
1067
1329
|
Type: 'AWS::EC2::SubnetRouteTableAssociation',
|
|
1068
1330
|
Properties: {
|
|
1069
1331
|
SubnetId: { Ref: 'FriggPrivateSubnet2' },
|
|
@@ -1072,7 +1334,10 @@ const configureVpc = (definition, AppDefinition, discoveredResources) => {
|
|
|
1072
1334
|
};
|
|
1073
1335
|
}
|
|
1074
1336
|
} else if (subnetManagement === 'use-existing') {
|
|
1075
|
-
if (
|
|
1337
|
+
if (
|
|
1338
|
+
!AppDefinition.vpc.subnets?.ids ||
|
|
1339
|
+
AppDefinition.vpc.subnets.ids.length < 2
|
|
1340
|
+
) {
|
|
1076
1341
|
throw new Error(
|
|
1077
1342
|
'Subnet management is "use-existing" but less than 2 subnet IDs provided. Provide at least 2 subnet IDs in vpc.subnets.ids.'
|
|
1078
1343
|
);
|
|
@@ -1082,13 +1347,19 @@ const configureVpc = (definition, AppDefinition, discoveredResources) => {
|
|
|
1082
1347
|
vpcConfig.subnetIds =
|
|
1083
1348
|
AppDefinition.vpc.subnets?.ids?.length > 0
|
|
1084
1349
|
? AppDefinition.vpc.subnets.ids
|
|
1085
|
-
: discoveredResources.privateSubnetId1 &&
|
|
1086
|
-
|
|
1087
|
-
|
|
1350
|
+
: discoveredResources.privateSubnetId1 &&
|
|
1351
|
+
discoveredResources.privateSubnetId2
|
|
1352
|
+
? [
|
|
1353
|
+
discoveredResources.privateSubnetId1,
|
|
1354
|
+
discoveredResources.privateSubnetId2,
|
|
1355
|
+
]
|
|
1356
|
+
: [];
|
|
1088
1357
|
|
|
1089
1358
|
if (vpcConfig.subnetIds.length < 2) {
|
|
1090
1359
|
if (AppDefinition.vpc.selfHeal) {
|
|
1091
|
-
console.log(
|
|
1360
|
+
console.log(
|
|
1361
|
+
'No subnets found but self-heal enabled - creating minimal subnet setup'
|
|
1362
|
+
);
|
|
1092
1363
|
subnetManagement = 'create';
|
|
1093
1364
|
discoveredResources.createSubnets = true;
|
|
1094
1365
|
} else {
|
|
@@ -1100,19 +1371,22 @@ const configureVpc = (definition, AppDefinition, discoveredResources) => {
|
|
|
1100
1371
|
}
|
|
1101
1372
|
|
|
1102
1373
|
if (subnetManagement === 'create' && discoveredResources.createSubnets) {
|
|
1103
|
-
definition.resources.Resources.FriggLambdaRouteTable =
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1374
|
+
definition.resources.Resources.FriggLambdaRouteTable = definition
|
|
1375
|
+
.resources.Resources.FriggLambdaRouteTable || {
|
|
1376
|
+
Type: 'AWS::EC2::RouteTable',
|
|
1377
|
+
Properties: {
|
|
1378
|
+
VpcId: effectiveVpcId,
|
|
1379
|
+
Tags: [
|
|
1380
|
+
{
|
|
1381
|
+
Key: 'Name',
|
|
1382
|
+
Value: '${self:service}-${self:provider.stage}-lambda-rt',
|
|
1383
|
+
},
|
|
1384
|
+
{ Key: 'ManagedBy', Value: 'Frigg' },
|
|
1385
|
+
{ Key: 'Environment', Value: '${self:provider.stage}' },
|
|
1386
|
+
{ Key: 'Service', Value: '${self:service}' },
|
|
1387
|
+
],
|
|
1388
|
+
},
|
|
1389
|
+
};
|
|
1116
1390
|
}
|
|
1117
1391
|
|
|
1118
1392
|
if (
|
|
@@ -1121,7 +1395,8 @@ const configureVpc = (definition, AppDefinition, discoveredResources) => {
|
|
|
1121
1395
|
) {
|
|
1122
1396
|
definition.provider.vpc = vpcConfig;
|
|
1123
1397
|
|
|
1124
|
-
const natGatewayManagement =
|
|
1398
|
+
const natGatewayManagement =
|
|
1399
|
+
AppDefinition.vpc.natGateway?.management || 'discover';
|
|
1125
1400
|
let needsNewNatGateway =
|
|
1126
1401
|
natGatewayManagement === 'createAndManage' ||
|
|
1127
1402
|
discoveredResources.needsNewNatGateway === true;
|
|
@@ -1132,7 +1407,9 @@ const configureVpc = (definition, AppDefinition, discoveredResources) => {
|
|
|
1132
1407
|
let useExistingEip = false;
|
|
1133
1408
|
|
|
1134
1409
|
if (needsNewNatGateway) {
|
|
1135
|
-
console.log(
|
|
1410
|
+
console.log(
|
|
1411
|
+
'Create mode: Creating dedicated EIP, public subnet, and NAT Gateway...'
|
|
1412
|
+
);
|
|
1136
1413
|
|
|
1137
1414
|
if (
|
|
1138
1415
|
discoveredResources.existingNatGatewayId &&
|
|
@@ -1140,12 +1417,18 @@ const configureVpc = (definition, AppDefinition, discoveredResources) => {
|
|
|
1140
1417
|
) {
|
|
1141
1418
|
console.log('Found existing Frigg-managed NAT Gateway and EIP');
|
|
1142
1419
|
if (!discoveredResources.natGatewayInPrivateSubnet) {
|
|
1143
|
-
console.log(
|
|
1420
|
+
console.log(
|
|
1421
|
+
'✅ Existing NAT Gateway is in PUBLIC subnet, will reuse it'
|
|
1422
|
+
);
|
|
1144
1423
|
reuseExistingNatGateway = true;
|
|
1145
1424
|
} else {
|
|
1146
|
-
console.log(
|
|
1425
|
+
console.log(
|
|
1426
|
+
'❌ NAT Gateway is in PRIVATE subnet - MUST create new one in PUBLIC subnet'
|
|
1427
|
+
);
|
|
1147
1428
|
if (AppDefinition.vpc.selfHeal) {
|
|
1148
|
-
console.log(
|
|
1429
|
+
console.log(
|
|
1430
|
+
'Self-heal enabled: Creating new NAT Gateway in PUBLIC subnet'
|
|
1431
|
+
);
|
|
1149
1432
|
reuseExistingNatGateway = false;
|
|
1150
1433
|
useExistingEip = false;
|
|
1151
1434
|
discoveredResources.needsCleanup = true;
|
|
@@ -1159,12 +1442,16 @@ const configureVpc = (definition, AppDefinition, discoveredResources) => {
|
|
|
1159
1442
|
discoveredResources.existingElasticIpAllocationId &&
|
|
1160
1443
|
!discoveredResources.existingNatGatewayId
|
|
1161
1444
|
) {
|
|
1162
|
-
console.log(
|
|
1445
|
+
console.log(
|
|
1446
|
+
'Found orphaned EIP, will reuse it for new NAT Gateway in PUBLIC subnet'
|
|
1447
|
+
);
|
|
1163
1448
|
useExistingEip = true;
|
|
1164
1449
|
}
|
|
1165
1450
|
|
|
1166
1451
|
if (reuseExistingNatGateway) {
|
|
1167
|
-
console.log(
|
|
1452
|
+
console.log(
|
|
1453
|
+
'Reusing existing NAT Gateway - skipping resource creation'
|
|
1454
|
+
);
|
|
1168
1455
|
} else {
|
|
1169
1456
|
if (!useExistingEip) {
|
|
1170
1457
|
definition.resources.Resources.FriggNATGatewayEIP = {
|
|
@@ -1174,10 +1461,16 @@ const configureVpc = (definition, AppDefinition, discoveredResources) => {
|
|
|
1174
1461
|
Properties: {
|
|
1175
1462
|
Domain: 'vpc',
|
|
1176
1463
|
Tags: [
|
|
1177
|
-
{
|
|
1464
|
+
{
|
|
1465
|
+
Key: 'Name',
|
|
1466
|
+
Value: '${self:service}-${self:provider.stage}-nat-eip',
|
|
1467
|
+
},
|
|
1178
1468
|
{ Key: 'ManagedBy', Value: 'Frigg' },
|
|
1179
1469
|
{ Key: 'Service', Value: '${self:service}' },
|
|
1180
|
-
{
|
|
1470
|
+
{
|
|
1471
|
+
Key: 'Stage',
|
|
1472
|
+
Value: '${self:provider.stage}',
|
|
1473
|
+
},
|
|
1181
1474
|
],
|
|
1182
1475
|
},
|
|
1183
1476
|
};
|
|
@@ -1185,25 +1478,34 @@ const configureVpc = (definition, AppDefinition, discoveredResources) => {
|
|
|
1185
1478
|
|
|
1186
1479
|
if (!discoveredResources.publicSubnetId) {
|
|
1187
1480
|
if (discoveredResources.internetGatewayId) {
|
|
1188
|
-
console.log(
|
|
1481
|
+
console.log(
|
|
1482
|
+
'Reusing existing Internet Gateway for NAT Gateway'
|
|
1483
|
+
);
|
|
1189
1484
|
} else {
|
|
1190
1485
|
definition.resources.Resources.FriggInternetGateway =
|
|
1191
|
-
definition.resources.Resources
|
|
1486
|
+
definition.resources.Resources
|
|
1487
|
+
.FriggInternetGateway || {
|
|
1192
1488
|
Type: 'AWS::EC2::InternetGateway',
|
|
1193
1489
|
Properties: {
|
|
1194
1490
|
Tags: [
|
|
1195
|
-
{
|
|
1491
|
+
{
|
|
1492
|
+
Key: 'Name',
|
|
1493
|
+
Value: '${self:service}-${self:provider.stage}-igw',
|
|
1494
|
+
},
|
|
1196
1495
|
{ Key: 'ManagedBy', Value: 'Frigg' },
|
|
1197
1496
|
],
|
|
1198
1497
|
},
|
|
1199
1498
|
};
|
|
1200
1499
|
|
|
1201
1500
|
definition.resources.Resources.FriggIGWAttachment =
|
|
1202
|
-
definition.resources.Resources
|
|
1501
|
+
definition.resources.Resources
|
|
1502
|
+
.FriggIGWAttachment || {
|
|
1203
1503
|
Type: 'AWS::EC2::VPCGatewayAttachment',
|
|
1204
1504
|
Properties: {
|
|
1205
1505
|
VpcId: discoveredResources.defaultVpcId,
|
|
1206
|
-
InternetGatewayId: {
|
|
1506
|
+
InternetGatewayId: {
|
|
1507
|
+
Ref: 'FriggInternetGateway',
|
|
1508
|
+
},
|
|
1207
1509
|
},
|
|
1208
1510
|
};
|
|
1209
1511
|
}
|
|
@@ -1213,11 +1515,17 @@ const configureVpc = (definition, AppDefinition, discoveredResources) => {
|
|
|
1213
1515
|
Properties: {
|
|
1214
1516
|
VpcId: discoveredResources.defaultVpcId,
|
|
1215
1517
|
CidrBlock:
|
|
1216
|
-
AppDefinition.vpc.natGateway
|
|
1217
|
-
|
|
1518
|
+
AppDefinition.vpc.natGateway
|
|
1519
|
+
?.publicSubnetCidr || '172.31.250.0/24',
|
|
1520
|
+
AvailabilityZone: {
|
|
1521
|
+
'Fn::Select': [0, { 'Fn::GetAZs': '' }],
|
|
1522
|
+
},
|
|
1218
1523
|
MapPublicIpOnLaunch: true,
|
|
1219
1524
|
Tags: [
|
|
1220
|
-
{
|
|
1525
|
+
{
|
|
1526
|
+
Key: 'Name',
|
|
1527
|
+
Value: '${self:service}-${self:provider.stage}-public-subnet',
|
|
1528
|
+
},
|
|
1221
1529
|
{ Key: 'Type', Value: 'Public' },
|
|
1222
1530
|
],
|
|
1223
1531
|
},
|
|
@@ -1228,22 +1536,31 @@ const configureVpc = (definition, AppDefinition, discoveredResources) => {
|
|
|
1228
1536
|
Properties: {
|
|
1229
1537
|
VpcId: discoveredResources.defaultVpcId,
|
|
1230
1538
|
Tags: [
|
|
1231
|
-
{
|
|
1539
|
+
{
|
|
1540
|
+
Key: 'Name',
|
|
1541
|
+
Value: '${self:service}-${self:provider.stage}-public-rt',
|
|
1542
|
+
},
|
|
1232
1543
|
],
|
|
1233
1544
|
},
|
|
1234
1545
|
};
|
|
1235
1546
|
|
|
1236
1547
|
definition.resources.Resources.FriggPublicRoute = {
|
|
1237
1548
|
Type: 'AWS::EC2::Route',
|
|
1238
|
-
DependsOn: discoveredResources.internetGatewayId
|
|
1549
|
+
DependsOn: discoveredResources.internetGatewayId
|
|
1550
|
+
? []
|
|
1551
|
+
: 'FriggIGWAttachment',
|
|
1239
1552
|
Properties: {
|
|
1240
1553
|
RouteTableId: { Ref: 'FriggPublicRouteTable' },
|
|
1241
1554
|
DestinationCidrBlock: '0.0.0.0/0',
|
|
1242
|
-
GatewayId:
|
|
1555
|
+
GatewayId:
|
|
1556
|
+
discoveredResources.internetGatewayId || {
|
|
1557
|
+
Ref: 'FriggInternetGateway',
|
|
1558
|
+
},
|
|
1243
1559
|
},
|
|
1244
1560
|
};
|
|
1245
1561
|
|
|
1246
|
-
definition.resources.Resources.FriggPublicSubnetRouteTableAssociation =
|
|
1562
|
+
definition.resources.Resources.FriggPublicSubnetRouteTableAssociation =
|
|
1563
|
+
{
|
|
1247
1564
|
Type: 'AWS::EC2::SubnetRouteTableAssociation',
|
|
1248
1565
|
Properties: {
|
|
1249
1566
|
SubnetId: { Ref: 'FriggPublicSubnet' },
|
|
@@ -1259,11 +1576,20 @@ const configureVpc = (definition, AppDefinition, discoveredResources) => {
|
|
|
1259
1576
|
Properties: {
|
|
1260
1577
|
AllocationId: useExistingEip
|
|
1261
1578
|
? discoveredResources.existingElasticIpAllocationId
|
|
1262
|
-
: {
|
|
1263
|
-
|
|
1264
|
-
|
|
1579
|
+
: {
|
|
1580
|
+
'Fn::GetAtt': [
|
|
1581
|
+
'FriggNATGatewayEIP',
|
|
1582
|
+
'AllocationId',
|
|
1583
|
+
],
|
|
1584
|
+
},
|
|
1585
|
+
SubnetId: discoveredResources.publicSubnetId || {
|
|
1586
|
+
Ref: 'FriggPublicSubnet',
|
|
1587
|
+
},
|
|
1265
1588
|
Tags: [
|
|
1266
|
-
{
|
|
1589
|
+
{
|
|
1590
|
+
Key: 'Name',
|
|
1591
|
+
Value: '${self:service}-${self:provider.stage}-nat-gateway',
|
|
1592
|
+
},
|
|
1267
1593
|
{ Key: 'ManagedBy', Value: 'Frigg' },
|
|
1268
1594
|
{ Key: 'Service', Value: '${self:service}' },
|
|
1269
1595
|
{ Key: 'Stage', Value: '${self:provider.stage}' },
|
|
@@ -1275,9 +1601,15 @@ const configureVpc = (definition, AppDefinition, discoveredResources) => {
|
|
|
1275
1601
|
natGatewayManagement === 'discover' ||
|
|
1276
1602
|
natGatewayManagement === 'useExisting'
|
|
1277
1603
|
) {
|
|
1278
|
-
if (
|
|
1279
|
-
|
|
1280
|
-
|
|
1604
|
+
if (
|
|
1605
|
+
natGatewayManagement === 'useExisting' &&
|
|
1606
|
+
AppDefinition.vpc.natGateway?.id
|
|
1607
|
+
) {
|
|
1608
|
+
console.log(
|
|
1609
|
+
`Using explicitly provided NAT Gateway: ${AppDefinition.vpc.natGateway.id}`
|
|
1610
|
+
);
|
|
1611
|
+
discoveredResources.existingNatGatewayId =
|
|
1612
|
+
AppDefinition.vpc.natGateway.id;
|
|
1281
1613
|
}
|
|
1282
1614
|
|
|
1283
1615
|
if (discoveredResources.existingNatGatewayId) {
|
|
@@ -1287,14 +1619,20 @@ const configureVpc = (definition, AppDefinition, discoveredResources) => {
|
|
|
1287
1619
|
);
|
|
1288
1620
|
|
|
1289
1621
|
if (discoveredResources.natGatewayInPrivateSubnet) {
|
|
1290
|
-
console.log(
|
|
1622
|
+
console.log(
|
|
1623
|
+
'❌ CRITICAL: NAT Gateway is in PRIVATE subnet - Internet connectivity will NOT work!'
|
|
1624
|
+
);
|
|
1291
1625
|
|
|
1292
1626
|
if (AppDefinition.vpc.selfHeal === true) {
|
|
1293
|
-
console.log(
|
|
1627
|
+
console.log(
|
|
1628
|
+
'Self-heal enabled: Will create new NAT Gateway in PUBLIC subnet'
|
|
1629
|
+
);
|
|
1294
1630
|
needsNewNatGateway = true;
|
|
1295
1631
|
discoveredResources.existingNatGatewayId = null;
|
|
1296
1632
|
if (!discoveredResources.publicSubnetId) {
|
|
1297
|
-
console.log(
|
|
1633
|
+
console.log(
|
|
1634
|
+
'No public subnet found - will create one for NAT Gateway'
|
|
1635
|
+
);
|
|
1298
1636
|
discoveredResources.createPublicSubnet = true;
|
|
1299
1637
|
}
|
|
1300
1638
|
} else {
|
|
@@ -1303,52 +1641,73 @@ const configureVpc = (definition, AppDefinition, discoveredResources) => {
|
|
|
1303
1641
|
);
|
|
1304
1642
|
}
|
|
1305
1643
|
} else {
|
|
1306
|
-
console.log(
|
|
1644
|
+
console.log(
|
|
1645
|
+
`Using discovered NAT Gateway for routing: ${discoveredResources.existingNatGatewayId}`
|
|
1646
|
+
);
|
|
1307
1647
|
}
|
|
1308
|
-
} else if (
|
|
1309
|
-
|
|
1310
|
-
|
|
1648
|
+
} else if (
|
|
1649
|
+
!needsNewNatGateway &&
|
|
1650
|
+
AppDefinition.vpc.natGateway?.id
|
|
1651
|
+
) {
|
|
1652
|
+
console.log(
|
|
1653
|
+
`Using explicitly provided NAT Gateway: ${AppDefinition.vpc.natGateway.id}`
|
|
1654
|
+
);
|
|
1655
|
+
discoveredResources.existingNatGatewayId =
|
|
1656
|
+
AppDefinition.vpc.natGateway.id;
|
|
1311
1657
|
}
|
|
1312
1658
|
}
|
|
1313
1659
|
|
|
1314
|
-
definition.resources.Resources.FriggLambdaRouteTable =
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1660
|
+
definition.resources.Resources.FriggLambdaRouteTable = definition
|
|
1661
|
+
.resources.Resources.FriggLambdaRouteTable || {
|
|
1662
|
+
Type: 'AWS::EC2::RouteTable',
|
|
1663
|
+
Properties: {
|
|
1664
|
+
VpcId: discoveredResources.defaultVpcId || vpcId,
|
|
1665
|
+
Tags: [
|
|
1666
|
+
{
|
|
1667
|
+
Key: 'Name',
|
|
1668
|
+
Value: '${self:service}-${self:provider.stage}-lambda-rt',
|
|
1669
|
+
},
|
|
1670
|
+
{ Key: 'ManagedBy', Value: 'Frigg' },
|
|
1671
|
+
{ Key: 'Environment', Value: '${self:provider.stage}' },
|
|
1672
|
+
{ Key: 'Service', Value: '${self:service}' },
|
|
1673
|
+
],
|
|
1674
|
+
},
|
|
1675
|
+
};
|
|
1327
1676
|
|
|
1328
1677
|
const routeTableId = { Ref: 'FriggLambdaRouteTable' };
|
|
1329
1678
|
let natGatewayIdForRoute;
|
|
1330
1679
|
|
|
1331
1680
|
if (reuseExistingNatGateway) {
|
|
1332
1681
|
natGatewayIdForRoute = discoveredResources.existingNatGatewayId;
|
|
1333
|
-
console.log(
|
|
1682
|
+
console.log(
|
|
1683
|
+
`Using discovered NAT Gateway for routing: ${natGatewayIdForRoute}`
|
|
1684
|
+
);
|
|
1334
1685
|
} else if (needsNewNatGateway && !reuseExistingNatGateway) {
|
|
1335
1686
|
natGatewayIdForRoute = { Ref: 'FriggNATGateway' };
|
|
1336
1687
|
console.log('Using newly created NAT Gateway for routing');
|
|
1337
1688
|
} else if (discoveredResources.existingNatGatewayId) {
|
|
1338
1689
|
natGatewayIdForRoute = discoveredResources.existingNatGatewayId;
|
|
1339
|
-
console.log(
|
|
1690
|
+
console.log(
|
|
1691
|
+
`Using discovered NAT Gateway for routing: ${natGatewayIdForRoute}`
|
|
1692
|
+
);
|
|
1340
1693
|
} else if (AppDefinition.vpc.natGateway?.id) {
|
|
1341
1694
|
natGatewayIdForRoute = AppDefinition.vpc.natGateway.id;
|
|
1342
|
-
console.log(
|
|
1695
|
+
console.log(
|
|
1696
|
+
`Using explicitly provided NAT Gateway for routing: ${natGatewayIdForRoute}`
|
|
1697
|
+
);
|
|
1343
1698
|
} else if (AppDefinition.vpc.selfHeal === true) {
|
|
1344
1699
|
natGatewayIdForRoute = null;
|
|
1345
|
-
console.log(
|
|
1700
|
+
console.log(
|
|
1701
|
+
'No NAT Gateway available - skipping NAT route creation'
|
|
1702
|
+
);
|
|
1346
1703
|
} else {
|
|
1347
1704
|
throw new Error('No existing NAT Gateway found in discovery mode');
|
|
1348
1705
|
}
|
|
1349
1706
|
|
|
1350
1707
|
if (natGatewayIdForRoute) {
|
|
1351
|
-
console.log(
|
|
1708
|
+
console.log(
|
|
1709
|
+
`Configuring NAT route: 0.0.0.0/0 → ${natGatewayIdForRoute}`
|
|
1710
|
+
);
|
|
1352
1711
|
definition.resources.Resources.FriggNATRoute = {
|
|
1353
1712
|
Type: 'AWS::EC2::Route',
|
|
1354
1713
|
DependsOn: 'FriggLambdaRouteTable',
|
|
@@ -1359,7 +1718,9 @@ const configureVpc = (definition, AppDefinition, discoveredResources) => {
|
|
|
1359
1718
|
},
|
|
1360
1719
|
};
|
|
1361
1720
|
} else {
|
|
1362
|
-
console.warn(
|
|
1721
|
+
console.warn(
|
|
1722
|
+
'⚠️ No NAT Gateway configured - Lambda functions will not have internet access'
|
|
1723
|
+
);
|
|
1363
1724
|
}
|
|
1364
1725
|
|
|
1365
1726
|
if (typeof vpcConfig.subnetIds[0] === 'string') {
|
|
@@ -1384,25 +1745,37 @@ const configureVpc = (definition, AppDefinition, discoveredResources) => {
|
|
|
1384
1745
|
};
|
|
1385
1746
|
}
|
|
1386
1747
|
|
|
1387
|
-
if (
|
|
1748
|
+
if (
|
|
1749
|
+
typeof vpcConfig.subnetIds[0] === 'object' &&
|
|
1750
|
+
vpcConfig.subnetIds[0].Ref
|
|
1751
|
+
) {
|
|
1388
1752
|
definition.resources.Resources.FriggNewSubnet1RouteAssociation = {
|
|
1389
1753
|
Type: 'AWS::EC2::SubnetRouteTableAssociation',
|
|
1390
1754
|
Properties: {
|
|
1391
1755
|
SubnetId: vpcConfig.subnetIds[0],
|
|
1392
1756
|
RouteTableId: routeTableId,
|
|
1393
1757
|
},
|
|
1394
|
-
DependsOn: [
|
|
1758
|
+
DependsOn: [
|
|
1759
|
+
'FriggLambdaRouteTable',
|
|
1760
|
+
vpcConfig.subnetIds[0].Ref,
|
|
1761
|
+
],
|
|
1395
1762
|
};
|
|
1396
1763
|
}
|
|
1397
1764
|
|
|
1398
|
-
if (
|
|
1765
|
+
if (
|
|
1766
|
+
typeof vpcConfig.subnetIds[1] === 'object' &&
|
|
1767
|
+
vpcConfig.subnetIds[1].Ref
|
|
1768
|
+
) {
|
|
1399
1769
|
definition.resources.Resources.FriggNewSubnet2RouteAssociation = {
|
|
1400
1770
|
Type: 'AWS::EC2::SubnetRouteTableAssociation',
|
|
1401
1771
|
Properties: {
|
|
1402
1772
|
SubnetId: vpcConfig.subnetIds[1],
|
|
1403
1773
|
RouteTableId: routeTableId,
|
|
1404
1774
|
},
|
|
1405
|
-
DependsOn: [
|
|
1775
|
+
DependsOn: [
|
|
1776
|
+
'FriggLambdaRouteTable',
|
|
1777
|
+
vpcConfig.subnetIds[1].Ref,
|
|
1778
|
+
],
|
|
1406
1779
|
};
|
|
1407
1780
|
}
|
|
1408
1781
|
|
|
@@ -1421,7 +1794,8 @@ const configureVpc = (definition, AppDefinition, discoveredResources) => {
|
|
|
1421
1794
|
Type: 'AWS::EC2::VPCEndpoint',
|
|
1422
1795
|
Properties: {
|
|
1423
1796
|
VpcId: discoveredResources.defaultVpcId,
|
|
1424
|
-
ServiceName:
|
|
1797
|
+
ServiceName:
|
|
1798
|
+
'com.amazonaws.${self:provider.region}.dynamodb',
|
|
1425
1799
|
VpcEndpointType: 'Gateway',
|
|
1426
1800
|
RouteTableIds: [routeTableId],
|
|
1427
1801
|
},
|
|
@@ -1438,7 +1812,10 @@ const configureVpc = (definition, AppDefinition, discoveredResources) => {
|
|
|
1438
1812
|
if (!definition.resources.Resources.VPCEndpointSecurityGroup) {
|
|
1439
1813
|
const vpcEndpointIngressRules = [];
|
|
1440
1814
|
|
|
1441
|
-
if (
|
|
1815
|
+
if (
|
|
1816
|
+
vpcConfig.securityGroupIds &&
|
|
1817
|
+
vpcConfig.securityGroupIds.length > 0
|
|
1818
|
+
) {
|
|
1442
1819
|
for (const sg of vpcConfig.securityGroupIds) {
|
|
1443
1820
|
if (typeof sg === 'string') {
|
|
1444
1821
|
vpcEndpointIngressRules.push({
|
|
@@ -1486,13 +1863,20 @@ const configureVpc = (definition, AppDefinition, discoveredResources) => {
|
|
|
1486
1863
|
definition.resources.Resources.VPCEndpointSecurityGroup = {
|
|
1487
1864
|
Type: 'AWS::EC2::SecurityGroup',
|
|
1488
1865
|
Properties: {
|
|
1489
|
-
GroupDescription:
|
|
1866
|
+
GroupDescription:
|
|
1867
|
+
'Security group for VPC endpoints - allows HTTPS from Lambda functions',
|
|
1490
1868
|
VpcId: discoveredResources.defaultVpcId,
|
|
1491
1869
|
SecurityGroupIngress: vpcEndpointIngressRules,
|
|
1492
1870
|
Tags: [
|
|
1493
|
-
{
|
|
1871
|
+
{
|
|
1872
|
+
Key: 'Name',
|
|
1873
|
+
Value: '${self:service}-${self:provider.stage}-vpc-endpoints-sg',
|
|
1874
|
+
},
|
|
1494
1875
|
{ Key: 'ManagedBy', Value: 'Frigg' },
|
|
1495
|
-
{
|
|
1876
|
+
{
|
|
1877
|
+
Key: 'Purpose',
|
|
1878
|
+
Value: 'Allow Lambda functions to access VPC endpoints',
|
|
1879
|
+
},
|
|
1496
1880
|
],
|
|
1497
1881
|
},
|
|
1498
1882
|
};
|
|
@@ -1515,7 +1899,8 @@ const configureVpc = (definition, AppDefinition, discoveredResources) => {
|
|
|
1515
1899
|
Type: 'AWS::EC2::VPCEndpoint',
|
|
1516
1900
|
Properties: {
|
|
1517
1901
|
VpcId: discoveredResources.defaultVpcId,
|
|
1518
|
-
ServiceName:
|
|
1902
|
+
ServiceName:
|
|
1903
|
+
'com.amazonaws.${self:provider.region}.secretsmanager',
|
|
1519
1904
|
VpcEndpointType: 'Interface',
|
|
1520
1905
|
SubnetIds: vpcConfig.subnetIds,
|
|
1521
1906
|
SecurityGroupIds: [{ Ref: 'VPCEndpointSecurityGroup' }],
|
|
@@ -1538,19 +1923,31 @@ const configureSsm = (definition, AppDefinition) => {
|
|
|
1538
1923
|
|
|
1539
1924
|
definition.provider.iamRoleStatements.push({
|
|
1540
1925
|
Effect: 'Allow',
|
|
1541
|
-
Action: [
|
|
1542
|
-
|
|
1926
|
+
Action: [
|
|
1927
|
+
'ssm:GetParameter',
|
|
1928
|
+
'ssm:GetParameters',
|
|
1929
|
+
'ssm:GetParametersByPath',
|
|
1930
|
+
],
|
|
1931
|
+
Resource: [
|
|
1932
|
+
'arn:aws:ssm:${self:provider.region}:*:parameter/${self:service}/${self:provider.stage}/*',
|
|
1933
|
+
],
|
|
1543
1934
|
});
|
|
1544
1935
|
|
|
1545
|
-
definition.provider.environment.SSM_PARAMETER_PREFIX =
|
|
1936
|
+
definition.provider.environment.SSM_PARAMETER_PREFIX =
|
|
1937
|
+
'/${self:service}/${self:provider.stage}';
|
|
1546
1938
|
};
|
|
1547
1939
|
|
|
1548
1940
|
const attachIntegrations = (definition, AppDefinition) => {
|
|
1549
|
-
if (
|
|
1941
|
+
if (
|
|
1942
|
+
!Array.isArray(AppDefinition.integrations) ||
|
|
1943
|
+
AppDefinition.integrations.length === 0
|
|
1944
|
+
) {
|
|
1550
1945
|
return;
|
|
1551
1946
|
}
|
|
1552
1947
|
|
|
1553
|
-
console.log(
|
|
1948
|
+
console.log(
|
|
1949
|
+
`Processing ${AppDefinition.integrations.length} integrations...`
|
|
1950
|
+
);
|
|
1554
1951
|
|
|
1555
1952
|
for (const integration of AppDefinition.integrations) {
|
|
1556
1953
|
if (!integration?.Definition?.name) {
|
|
@@ -1558,7 +1955,8 @@ const attachIntegrations = (definition, AppDefinition) => {
|
|
|
1558
1955
|
}
|
|
1559
1956
|
|
|
1560
1957
|
const integrationName = integration.Definition.name;
|
|
1561
|
-
const queueReference = `${integrationName.charAt(0).toUpperCase() + integrationName.slice(1)
|
|
1958
|
+
const queueReference = `${integrationName.charAt(0).toUpperCase() + integrationName.slice(1)
|
|
1959
|
+
}Queue`;
|
|
1562
1960
|
const queueName = `\${self:service}--\${self:provider.stage}-${queueReference}`;
|
|
1563
1961
|
|
|
1564
1962
|
definition.functions[integrationName] = {
|
|
@@ -1581,7 +1979,9 @@ const attachIntegrations = (definition, AppDefinition) => {
|
|
|
1581
1979
|
VisibilityTimeout: 1800,
|
|
1582
1980
|
RedrivePolicy: {
|
|
1583
1981
|
maxReceiveCount: 1,
|
|
1584
|
-
deadLetterTargetArn: {
|
|
1982
|
+
deadLetterTargetArn: {
|
|
1983
|
+
'Fn::GetAtt': ['InternalErrorQueue', 'Arn'],
|
|
1984
|
+
},
|
|
1585
1985
|
},
|
|
1586
1986
|
},
|
|
1587
1987
|
};
|
|
@@ -1603,10 +2003,36 @@ const attachIntegrations = (definition, AppDefinition) => {
|
|
|
1603
2003
|
|
|
1604
2004
|
definition.provider.environment = {
|
|
1605
2005
|
...definition.provider.environment,
|
|
1606
|
-
[`${integrationName.toUpperCase()}_QUEUE_URL`]: {
|
|
2006
|
+
[`${integrationName.toUpperCase()}_QUEUE_URL`]: {
|
|
2007
|
+
Ref: queueReference,
|
|
2008
|
+
},
|
|
1607
2009
|
};
|
|
1608
2010
|
|
|
1609
2011
|
definition.custom[queueReference] = queueName;
|
|
2012
|
+
|
|
2013
|
+
// Add webhook handler if enabled
|
|
2014
|
+
const webhookConfig = integration.Definition.webhooks;
|
|
2015
|
+
if (webhookConfig && (webhookConfig === true || webhookConfig.enabled === true)) {
|
|
2016
|
+
const webhookFunctionName = `${integrationName}Webhook`;
|
|
2017
|
+
|
|
2018
|
+
definition.functions[webhookFunctionName] = {
|
|
2019
|
+
handler: `node_modules/@friggframework/core/handlers/routers/integration-webhook-routers.handlers.${integrationName}Webhook.handler`,
|
|
2020
|
+
events: [
|
|
2021
|
+
{
|
|
2022
|
+
httpApi: {
|
|
2023
|
+
path: `/api/${integrationName}-integration/webhooks`,
|
|
2024
|
+
method: 'POST',
|
|
2025
|
+
},
|
|
2026
|
+
},
|
|
2027
|
+
{
|
|
2028
|
+
httpApi: {
|
|
2029
|
+
path: `/api/${integrationName}-integration/webhooks/{integrationId}`,
|
|
2030
|
+
method: 'POST',
|
|
2031
|
+
},
|
|
2032
|
+
},
|
|
2033
|
+
],
|
|
2034
|
+
};
|
|
2035
|
+
}
|
|
1610
2036
|
}
|
|
1611
2037
|
};
|
|
1612
2038
|
|
|
@@ -1616,7 +2042,8 @@ const configureWebsockets = (definition, AppDefinition) => {
|
|
|
1616
2042
|
}
|
|
1617
2043
|
|
|
1618
2044
|
definition.functions.defaultWebsocket = {
|
|
1619
|
-
handler:
|
|
2045
|
+
handler:
|
|
2046
|
+
'node_modules/@friggframework/core/handlers/routers/websocket.handler',
|
|
1620
2047
|
events: [
|
|
1621
2048
|
{ websocket: { route: '$connect' } },
|
|
1622
2049
|
{ websocket: { route: '$default' } },
|
|
@@ -1630,11 +2057,32 @@ const composeServerlessDefinition = async (AppDefinition) => {
|
|
|
1630
2057
|
|
|
1631
2058
|
const discoveredResources = await gatherDiscoveredResources(AppDefinition);
|
|
1632
2059
|
const appEnvironmentVars = getAppEnvironmentVars(AppDefinition);
|
|
1633
|
-
const definition = createBaseDefinition(
|
|
2060
|
+
const definition = createBaseDefinition(
|
|
2061
|
+
AppDefinition,
|
|
2062
|
+
appEnvironmentVars,
|
|
2063
|
+
discoveredResources
|
|
2064
|
+
);
|
|
2065
|
+
|
|
2066
|
+
// Check if we're in local build mode (AWS discovery was skipped)
|
|
2067
|
+
const isLocalBuild = !shouldRunDiscovery(AppDefinition);
|
|
2068
|
+
|
|
2069
|
+
if (isLocalBuild) {
|
|
2070
|
+
console.log(
|
|
2071
|
+
'🏠 Local build mode detected - skipping AWS-dependent configurations'
|
|
2072
|
+
);
|
|
2073
|
+
}
|
|
2074
|
+
|
|
2075
|
+
// Apply configurations (skip AWS-dependent ones in local build mode)
|
|
2076
|
+
if (!isLocalBuild) {
|
|
2077
|
+
applyKmsConfiguration(definition, AppDefinition, discoveredResources);
|
|
2078
|
+
configureVpc(definition, AppDefinition, discoveredResources);
|
|
2079
|
+
configureSsm(definition, AppDefinition);
|
|
2080
|
+
} else {
|
|
2081
|
+
console.log(
|
|
2082
|
+
' ⏭️ Skipping: KMS, VPC, PostgreSQL, SSM configurations'
|
|
2083
|
+
);
|
|
2084
|
+
}
|
|
1634
2085
|
|
|
1635
|
-
applyKmsConfiguration(definition, AppDefinition, discoveredResources);
|
|
1636
|
-
configureVpc(definition, AppDefinition, discoveredResources);
|
|
1637
|
-
configureSsm(definition, AppDefinition);
|
|
1638
2086
|
attachIntegrations(definition, AppDefinition);
|
|
1639
2087
|
configureWebsockets(definition, AppDefinition);
|
|
1640
2088
|
|