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