@friggframework/devtools 2.0.0--canary.428.5364e8f.0 → 2.0.0--canary.428.5c4220d.0

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.
@@ -1,22 +1,45 @@
1
- let EC2Client, DescribeVpcsCommand, DescribeSubnetsCommand, DescribeSecurityGroupsCommand, DescribeRouteTablesCommand, DescribeNatGatewaysCommand, DescribeAddressesCommand, DescribeInternetGatewaysCommand;
1
+ let EC2Client,
2
+ DescribeVpcsCommand,
3
+ DescribeSubnetsCommand,
4
+ DescribeSecurityGroupsCommand,
5
+ DescribeRouteTablesCommand,
6
+ DescribeNatGatewaysCommand,
7
+ DescribeAddressesCommand,
8
+ DescribeInternetGatewaysCommand;
2
9
  let KMSClient, ListKeysCommand, DescribeKeyCommand;
3
10
  let STSClient, GetCallerIdentityCommand;
4
11
 
5
12
  function loadEC2() {
6
13
  if (!EC2Client) {
7
- ({ EC2Client, DescribeVpcsCommand, DescribeSubnetsCommand, DescribeSecurityGroupsCommand, DescribeRouteTablesCommand, DescribeNatGatewaysCommand, DescribeAddressesCommand, DescribeInternetGatewaysCommand } = require('@aws-sdk/client-ec2'));
14
+ ({
15
+ EC2Client,
16
+ DescribeVpcsCommand,
17
+ DescribeSubnetsCommand,
18
+ DescribeSecurityGroupsCommand,
19
+ DescribeRouteTablesCommand,
20
+ DescribeNatGatewaysCommand,
21
+ DescribeAddressesCommand,
22
+ DescribeInternetGatewaysCommand,
23
+ } = require('@aws-sdk/client-ec2'));
8
24
  }
9
25
  }
10
26
 
11
27
  function loadKMS() {
12
28
  if (!KMSClient) {
13
- ({ KMSClient, ListKeysCommand, DescribeKeyCommand } = require('@aws-sdk/client-kms'));
29
+ ({
30
+ KMSClient,
31
+ ListKeysCommand,
32
+ DescribeKeyCommand,
33
+ } = require('@aws-sdk/client-kms'));
14
34
  }
15
35
  }
16
36
 
17
37
  function loadSTS() {
18
38
  if (!STSClient) {
19
- ({ STSClient, GetCallerIdentityCommand } = require('@aws-sdk/client-sts'));
39
+ ({
40
+ STSClient,
41
+ GetCallerIdentityCommand,
42
+ } = require('@aws-sdk/client-sts'));
20
43
  }
21
44
  }
22
45
 
@@ -66,26 +89,26 @@ class AWSDiscovery {
66
89
  Filters: [
67
90
  {
68
91
  Name: 'is-default',
69
- Values: ['true']
70
- }
71
- ]
92
+ Values: ['true'],
93
+ },
94
+ ],
72
95
  });
73
-
96
+
74
97
  const response = await this.ec2Client.send(command);
75
-
98
+
76
99
  if (response.Vpcs && response.Vpcs.length > 0) {
77
100
  return response.Vpcs[0];
78
101
  }
79
-
102
+
80
103
  // If no default VPC, get the first available VPC
81
104
  const allVpcsCommand = new DescribeVpcsCommand({});
82
105
  const allVpcsResponse = await this.ec2Client.send(allVpcsCommand);
83
-
106
+
84
107
  if (allVpcsResponse.Vpcs && allVpcsResponse.Vpcs.length > 0) {
85
108
  console.log('No default VPC found, using first available VPC');
86
109
  return allVpcsResponse.Vpcs[0];
87
110
  }
88
-
111
+
89
112
  throw new Error('No VPC found in the account');
90
113
  } catch (error) {
91
114
  console.error('Error finding default VPC:', error.message);
@@ -106,9 +129,9 @@ class AWSDiscovery {
106
129
  Filters: [
107
130
  {
108
131
  Name: 'vpc-id',
109
- Values: [vpcId]
110
- }
111
- ]
132
+ Values: [vpcId],
133
+ },
134
+ ],
112
135
  });
113
136
 
114
137
  const response = await this.ec2Client.send(command);
@@ -117,7 +140,9 @@ class AWSDiscovery {
117
140
  throw new Error(`No subnets found in VPC ${vpcId}`);
118
141
  }
119
142
 
120
- console.log(`\nšŸ” Analyzing ${response.Subnets.length} subnets in VPC ${vpcId}...`);
143
+ console.log(
144
+ `\nšŸ” Analyzing ${response.Subnets.length} subnets in VPC ${vpcId}...`
145
+ );
121
146
 
122
147
  // Categorize subnets by their actual routing
123
148
  const privateSubnets = [];
@@ -128,10 +153,14 @@ class AWSDiscovery {
128
153
  const isPrivate = await this.isSubnetPrivate(subnet.SubnetId);
129
154
  if (isPrivate) {
130
155
  privateSubnets.push(subnet);
131
- console.log(` šŸ”’ Private subnet: ${subnet.SubnetId} (AZ: ${subnet.AvailabilityZone})`);
156
+ console.log(
157
+ ` šŸ”’ Private subnet: ${subnet.SubnetId} (AZ: ${subnet.AvailabilityZone})`
158
+ );
132
159
  } else {
133
160
  publicSubnets.push(subnet);
134
- console.log(` 🌐 Public subnet: ${subnet.SubnetId} (AZ: ${subnet.AvailabilityZone})`);
161
+ console.log(
162
+ ` 🌐 Public subnet: ${subnet.SubnetId} (AZ: ${subnet.AvailabilityZone})`
163
+ );
135
164
  }
136
165
  }
137
166
 
@@ -141,15 +170,21 @@ class AWSDiscovery {
141
170
 
142
171
  // If we have at least 2 private subnets, use them
143
172
  if (privateSubnets.length >= 2) {
144
- console.log(`āœ… Found ${privateSubnets.length} private subnets for Lambda deployment`);
173
+ console.log(
174
+ `āœ… Found ${privateSubnets.length} private subnets for Lambda deployment`
175
+ );
145
176
  return privateSubnets.slice(0, 2);
146
177
  }
147
178
 
148
179
  // If we have 1 private subnet, we need at least one more
149
180
  if (privateSubnets.length === 1) {
150
- console.warn(`āš ļø Only 1 private subnet found. Need at least 2 for high availability.`);
181
+ console.warn(
182
+ `āš ļø Only 1 private subnet found. Need at least 2 for high availability.`
183
+ );
151
184
  if (publicSubnets.length > 0 && autoConvert) {
152
- console.log(`šŸ”„ Will convert 1 public subnet to private for high availability...`);
185
+ console.log(
186
+ `šŸ”„ Will convert 1 public subnet to private for high availability...`
187
+ );
153
188
  // Note: The actual conversion happens in the serverless template
154
189
  }
155
190
  // Return what we have - mix of private and public if needed
@@ -158,38 +193,62 @@ class AWSDiscovery {
158
193
 
159
194
  // No private subnets found at all - this is a problem!
160
195
  if (privateSubnets.length === 0 && publicSubnets.length > 0) {
161
- console.error(`āŒ CRITICAL: No private subnets found, but ${publicSubnets.length} public subnets exist`);
162
- console.error(`āŒ Lambda functions should NOT be deployed in public subnets!`);
196
+ console.error(
197
+ `āŒ CRITICAL: No private subnets found, but ${publicSubnets.length} public subnets exist`
198
+ );
199
+ console.error(
200
+ `āŒ Lambda functions should NOT be deployed in public subnets!`
201
+ );
163
202
 
164
203
  if (autoConvert && publicSubnets.length >= 3) {
165
- console.log(`\nšŸ”§ AUTO-CONVERSION: Will configure subnets for proper isolation...`);
166
- console.log(` - Keeping ${publicSubnets[0].SubnetId} as public (for NAT Gateway)`);
167
- console.log(` - Converting ${publicSubnets[1].SubnetId} to private (for Lambda)`);
204
+ console.log(
205
+ `\nšŸ”§ AUTO-CONVERSION: Will configure subnets for proper isolation...`
206
+ );
207
+ console.log(
208
+ ` - Keeping ${publicSubnets[0].SubnetId} as public (for NAT Gateway)`
209
+ );
210
+ console.log(
211
+ ` - Converting ${publicSubnets[1].SubnetId} to private (for Lambda)`
212
+ );
168
213
  if (publicSubnets[2]) {
169
- console.log(` - Converting ${publicSubnets[2].SubnetId} to private (for Lambda)`);
214
+ console.log(
215
+ ` - Converting ${publicSubnets[2].SubnetId} to private (for Lambda)`
216
+ );
170
217
  }
171
218
 
172
219
  // Return subnets that SHOULD be private (indexes 1 and 2)
173
220
  // The actual conversion happens in the serverless template
174
221
  return publicSubnets.slice(1, 3);
175
222
  } else if (autoConvert && publicSubnets.length >= 2) {
176
- console.log(`\nšŸ”§ AUTO-CONVERSION: Only ${publicSubnets.length} subnets available`);
177
- console.log(` - Will need to create new subnets or reconfigure existing ones`);
223
+ console.log(
224
+ `\nšŸ”§ AUTO-CONVERSION: Only ${publicSubnets.length} subnets available`
225
+ );
226
+ console.log(
227
+ ` - Will need to create new subnets or reconfigure existing ones`
228
+ );
178
229
  // Return what we have but flag for conversion
179
230
  return publicSubnets.slice(0, 2);
180
231
  } else {
181
232
  console.error(`\nāš ļø CONFIGURATION ERROR:`);
182
- console.error(` Found ${publicSubnets.length} public subnets but no private subnets.`);
183
- console.error(` Lambda functions require private subnets for security.`);
233
+ console.error(
234
+ ` Found ${publicSubnets.length} public subnets but no private subnets.`
235
+ );
236
+ console.error(
237
+ ` Lambda functions require private subnets for security.`
238
+ );
184
239
  console.error(`\n Options:`);
185
- console.error(` 1. Enable selfHeal: true in vpc configuration`);
240
+ console.error(
241
+ ` 1. Enable selfHeal: true in vpc configuration`
242
+ );
186
243
  console.error(` 2. Create private subnets manually`);
187
- console.error(` 3. Set subnets.management: 'create' to create new private subnets`);
244
+ console.error(
245
+ ` 3. Set subnets.management: 'create' to create new private subnets`
246
+ );
188
247
 
189
248
  throw new Error(
190
249
  `No private subnets found in VPC ${vpcId}. ` +
191
- `Found ${publicSubnets.length} public subnets. ` +
192
- `Lambda requires private subnets. Enable selfHeal or create private subnets.`
250
+ `Found ${publicSubnets.length} public subnets. ` +
251
+ `Lambda requires private subnets. Enable selfHeal or create private subnets.`
193
252
  );
194
253
  }
195
254
  }
@@ -221,32 +280,37 @@ class AWSDiscovery {
221
280
  try {
222
281
  // First, get the subnet details to find its VPC
223
282
  const subnetCommand = new DescribeSubnetsCommand({
224
- SubnetIds: [subnetId]
283
+ SubnetIds: [subnetId],
225
284
  });
226
285
  const subnetResponse = await this.ec2Client.send(subnetCommand);
227
-
228
- if (!subnetResponse.Subnets || subnetResponse.Subnets.length === 0) {
286
+
287
+ if (
288
+ !subnetResponse.Subnets ||
289
+ subnetResponse.Subnets.length === 0
290
+ ) {
229
291
  throw new Error(`Subnet ${subnetId} not found`);
230
292
  }
231
-
293
+
232
294
  const subnet = subnetResponse.Subnets[0];
233
295
  const vpcId = subnet.VpcId;
234
-
296
+
235
297
  // Get all route tables for this VPC
236
298
  const routeTablesCommand = new DescribeRouteTablesCommand({
237
299
  Filters: [
238
300
  {
239
301
  Name: 'vpc-id',
240
- Values: [vpcId]
241
- }
242
- ]
302
+ Values: [vpcId],
303
+ },
304
+ ],
243
305
  });
244
-
245
- const routeTablesResponse = await this.ec2Client.send(routeTablesCommand);
246
-
306
+
307
+ const routeTablesResponse = await this.ec2Client.send(
308
+ routeTablesCommand
309
+ );
310
+
247
311
  // Find the route table for this subnet
248
312
  let routeTable = null;
249
-
313
+
250
314
  // First check for explicit association
251
315
  for (const rt of routeTablesResponse.RouteTables || []) {
252
316
  for (const assoc of rt.Associations || []) {
@@ -257,7 +321,7 @@ class AWSDiscovery {
257
321
  }
258
322
  if (routeTable) break;
259
323
  }
260
-
324
+
261
325
  // If no explicit association, use the main route table
262
326
  if (!routeTable) {
263
327
  for (const rt of routeTablesResponse.RouteTables || []) {
@@ -270,12 +334,12 @@ class AWSDiscovery {
270
334
  if (routeTable) break;
271
335
  }
272
336
  }
273
-
337
+
274
338
  if (!routeTable) {
275
339
  console.warn(`No route table found for subnet ${subnetId}`);
276
340
  return true; // Default to private for safety
277
341
  }
278
-
342
+
279
343
  // Check if route table has a route to an Internet Gateway
280
344
  let hasIgwRoute = false;
281
345
  let gatewayId = null;
@@ -290,14 +354,21 @@ class AWSDiscovery {
290
354
 
291
355
  // Enhanced logging for validation
292
356
  if (hasIgwRoute) {
293
- console.log(`āœ… Subnet ${subnetId} is PUBLIC (has route to IGW ${gatewayId})`);
357
+ console.log(
358
+ `āœ… Subnet ${subnetId} is PUBLIC (has route to IGW ${gatewayId})`
359
+ );
294
360
  return false; // It's a public subnet
295
361
  } else {
296
- console.log(`šŸ”’ Subnet ${subnetId} is PRIVATE (no IGW route found)`);
362
+ console.log(
363
+ `šŸ”’ Subnet ${subnetId} is PRIVATE (no IGW route found)`
364
+ );
297
365
  return true; // No IGW route found, it's private
298
366
  }
299
367
  } catch (error) {
300
- console.warn(`Could not determine if subnet ${subnetId} is private:`, error);
368
+ console.warn(
369
+ `Could not determine if subnet ${subnetId} is private:`,
370
+ error
371
+ );
301
372
  return true; // Default to private for safety
302
373
  }
303
374
  }
@@ -315,17 +386,20 @@ class AWSDiscovery {
315
386
  Filters: [
316
387
  {
317
388
  Name: 'vpc-id',
318
- Values: [vpcId]
389
+ Values: [vpcId],
319
390
  },
320
391
  {
321
392
  Name: 'group-name',
322
- Values: ['frigg-lambda-sg']
323
- }
324
- ]
393
+ Values: ['frigg-lambda-sg'],
394
+ },
395
+ ],
325
396
  });
326
-
397
+
327
398
  const friggResponse = await this.ec2Client.send(friggSgCommand);
328
- if (friggResponse.SecurityGroups && friggResponse.SecurityGroups.length > 0) {
399
+ if (
400
+ friggResponse.SecurityGroups &&
401
+ friggResponse.SecurityGroups.length > 0
402
+ ) {
329
403
  return friggResponse.SecurityGroups[0];
330
404
  }
331
405
 
@@ -334,20 +408,23 @@ class AWSDiscovery {
334
408
  Filters: [
335
409
  {
336
410
  Name: 'vpc-id',
337
- Values: [vpcId]
411
+ Values: [vpcId],
338
412
  },
339
413
  {
340
414
  Name: 'group-name',
341
- Values: ['default']
342
- }
343
- ]
415
+ Values: ['default'],
416
+ },
417
+ ],
344
418
  });
345
-
419
+
346
420
  const defaultResponse = await this.ec2Client.send(defaultSgCommand);
347
- if (defaultResponse.SecurityGroups && defaultResponse.SecurityGroups.length > 0) {
421
+ if (
422
+ defaultResponse.SecurityGroups &&
423
+ defaultResponse.SecurityGroups.length > 0
424
+ ) {
348
425
  return defaultResponse.SecurityGroups[0];
349
426
  }
350
-
427
+
351
428
  throw new Error(`No security group found for VPC ${vpcId}`);
352
429
  } catch (error) {
353
430
  console.error('Error finding default security group:', error);
@@ -367,9 +444,9 @@ class AWSDiscovery {
367
444
  Filters: [
368
445
  {
369
446
  Name: 'vpc-id',
370
- Values: [vpcId]
371
- }
372
- ]
447
+ Values: [vpcId],
448
+ },
449
+ ],
373
450
  });
374
451
 
375
452
  const response = await this.ec2Client.send(command);
@@ -391,14 +468,22 @@ class AWSDiscovery {
391
468
 
392
469
  if (publicSubnets.length === 0) {
393
470
  // If no public subnets found, we need to create one or inform the user
394
- console.warn(`WARNING: No public subnets found in VPC ${vpcId}`);
395
- console.warn('A public subnet with Internet Gateway route is required for NAT Gateway placement');
396
- console.warn('Please create a public subnet or use VPC endpoints instead');
471
+ console.warn(
472
+ `WARNING: No public subnets found in VPC ${vpcId}`
473
+ );
474
+ console.warn(
475
+ 'A public subnet with Internet Gateway route is required for NAT Gateway placement'
476
+ );
477
+ console.warn(
478
+ 'Please create a public subnet or use VPC endpoints instead'
479
+ );
397
480
  return null; // Return null instead of throwing to allow graceful handling
398
481
  }
399
482
 
400
483
  // Return first public subnet for NAT Gateway
401
- console.log(`Found ${publicSubnets.length} public subnets, using ${publicSubnets[0].SubnetId} for NAT Gateway`);
484
+ console.log(
485
+ `Found ${publicSubnets.length} public subnets, using ${publicSubnets[0].SubnetId} for NAT Gateway`
486
+ );
402
487
  return publicSubnets[0];
403
488
  } catch (error) {
404
489
  console.error('Error finding public subnets:', error);
@@ -418,13 +503,13 @@ class AWSDiscovery {
418
503
  Filters: [
419
504
  {
420
505
  Name: 'vpc-id',
421
- Values: [vpcId]
422
- }
423
- ]
506
+ Values: [vpcId],
507
+ },
508
+ ],
424
509
  });
425
-
510
+
426
511
  const response = await this.ec2Client.send(command);
427
-
512
+
428
513
  if (!response.RouteTables || response.RouteTables.length === 0) {
429
514
  throw new Error(`No route tables found for VPC ${vpcId}`);
430
515
  }
@@ -462,13 +547,13 @@ class AWSDiscovery {
462
547
  Filter: [
463
548
  {
464
549
  Name: 'vpc-id',
465
- Values: [vpcId]
550
+ Values: [vpcId],
466
551
  },
467
552
  {
468
553
  Name: 'state',
469
- Values: ['available']
470
- }
471
- ]
554
+ Values: ['available'],
555
+ },
556
+ ],
472
557
  });
473
558
 
474
559
  const response = await this.ec2Client.send(command);
@@ -476,14 +561,24 @@ class AWSDiscovery {
476
561
  if (response.NatGateways && response.NatGateways.length > 0) {
477
562
  // Sort NAT Gateways to prioritize Frigg-managed ones
478
563
  const sortedNatGateways = response.NatGateways.sort((a, b) => {
479
- const aIsFrigg = a.Tags && a.Tags.some(tag =>
480
- (tag.Key === 'ManagedBy' && tag.Value === 'Frigg') ||
481
- (tag.Key === 'Name' && tag.Value.includes('frigg'))
482
- );
483
- const bIsFrigg = b.Tags && b.Tags.some(tag =>
484
- (tag.Key === 'ManagedBy' && tag.Value === 'Frigg') ||
485
- (tag.Key === 'Name' && tag.Value.includes('frigg'))
486
- );
564
+ const aIsFrigg =
565
+ a.Tags &&
566
+ a.Tags.some(
567
+ (tag) =>
568
+ (tag.Key === 'ManagedBy' &&
569
+ tag.Value === 'Frigg') ||
570
+ (tag.Key === 'Name' &&
571
+ tag.Value.includes('frigg'))
572
+ );
573
+ const bIsFrigg =
574
+ b.Tags &&
575
+ b.Tags.some(
576
+ (tag) =>
577
+ (tag.Key === 'ManagedBy' &&
578
+ tag.Value === 'Frigg') ||
579
+ (tag.Key === 'Name' &&
580
+ tag.Value.includes('frigg'))
581
+ );
487
582
 
488
583
  if (aIsFrigg && !bIsFrigg) return -1;
489
584
  if (!aIsFrigg && bIsFrigg) return 1;
@@ -496,46 +591,71 @@ class AWSDiscovery {
496
591
  const isPrivate = await this.isSubnetPrivate(subnetId);
497
592
 
498
593
  // Check if it's a Frigg-managed NAT Gateway
499
- const isFriggNat = natGateway.Tags && natGateway.Tags.some(tag =>
500
- (tag.Key === 'ManagedBy' && tag.Value === 'Frigg') ||
501
- (tag.Key === 'Name' && tag.Value.includes('frigg'))
502
- );
594
+ const isFriggNat =
595
+ natGateway.Tags &&
596
+ natGateway.Tags.some(
597
+ (tag) =>
598
+ (tag.Key === 'ManagedBy' &&
599
+ tag.Value === 'Frigg') ||
600
+ (tag.Key === 'Name' &&
601
+ tag.Value.includes('frigg'))
602
+ );
503
603
 
504
604
  if (isPrivate) {
505
605
  // NAT Gateway appears to be in a private subnet
506
606
  // This could be due to route table misconfiguration
507
- console.warn(`WARNING: NAT Gateway ${natGateway.NatGatewayId} is in subnet ${subnetId} which appears to be private`);
607
+ console.warn(
608
+ `WARNING: NAT Gateway ${natGateway.NatGatewayId} is in subnet ${subnetId} which appears to be private`
609
+ );
508
610
 
509
611
  if (isFriggNat) {
510
- console.warn('This is a Frigg-managed NAT Gateway that may have been misconfigured by route table changes');
511
- console.warn('Consider enabling selfHeal: true to fix this automatically');
612
+ console.warn(
613
+ 'This is a Frigg-managed NAT Gateway that may have been misconfigured by route table changes'
614
+ );
615
+ console.warn(
616
+ 'Consider enabling selfHeal: true to fix this automatically'
617
+ );
512
618
  // Return it anyway if it's Frigg-managed - we can fix the routes
513
619
  // Mark that it's in a private subnet
514
620
  natGateway._isInPrivateSubnet = true;
515
621
  return natGateway;
516
622
  } else {
517
- console.warn('NAT Gateways MUST be placed in public subnets with Internet Gateway routes');
518
- console.warn('Skipping this misconfigured NAT Gateway...');
623
+ console.warn(
624
+ 'NAT Gateways MUST be placed in public subnets with Internet Gateway routes'
625
+ );
626
+ console.warn(
627
+ 'Skipping this misconfigured NAT Gateway...'
628
+ );
519
629
  continue; // Skip non-Frigg NAT Gateways in private subnets
520
630
  }
521
631
  }
522
632
 
523
633
  if (isFriggNat) {
524
- console.log(`Found existing Frigg-managed NAT Gateway: ${natGateway.NatGatewayId}`);
634
+ console.log(
635
+ `Found existing Frigg-managed NAT Gateway: ${natGateway.NatGatewayId}`
636
+ );
525
637
  natGateway._isInPrivateSubnet = false;
526
638
  return natGateway;
527
639
  }
528
640
 
529
641
  // Return first valid NAT Gateway that's in a public subnet
530
- console.log(`Found existing NAT Gateway in public subnet: ${natGateway.NatGatewayId}`);
642
+ console.log(
643
+ `Found existing NAT Gateway in public subnet: ${natGateway.NatGatewayId}`
644
+ );
531
645
  natGateway._isInPrivateSubnet = false;
532
646
  return natGateway;
533
647
  }
534
648
 
535
649
  // All non-Frigg NAT Gateways are in private subnets
536
- console.error(`ERROR: Found ${response.NatGateways.length} NAT Gateway(s) but all non-Frigg ones are in private subnets!`);
537
- console.error('These NAT Gateways will not provide internet connectivity without route table fixes');
538
- console.error('Enable selfHeal: true to fix automatically or create a new NAT Gateway');
650
+ console.error(
651
+ `ERROR: Found ${response.NatGateways.length} NAT Gateway(s) but all non-Frigg ones are in private subnets!`
652
+ );
653
+ console.error(
654
+ 'These NAT Gateways will not provide internet connectivity without route table fixes'
655
+ );
656
+ console.error(
657
+ 'Enable selfHeal: true to fix automatically or create a new NAT Gateway'
658
+ );
539
659
  return null; // Return null to trigger creation of new NAT Gateway
540
660
  }
541
661
 
@@ -554,31 +674,42 @@ class AWSDiscovery {
554
674
  try {
555
675
  const command = new DescribeAddressesCommand({});
556
676
  const response = await this.ec2Client.send(command);
557
-
677
+
558
678
  if (response.Addresses && response.Addresses.length > 0) {
559
679
  // Find an unassociated EIP first
560
- const availableEIP = response.Addresses.find(eip =>
561
- !eip.AssociationId && !eip.InstanceId && !eip.NetworkInterfaceId
680
+ const availableEIP = response.Addresses.find(
681
+ (eip) =>
682
+ !eip.AssociationId &&
683
+ !eip.InstanceId &&
684
+ !eip.NetworkInterfaceId
562
685
  );
563
-
686
+
564
687
  if (availableEIP) {
565
- console.log(`Found available Elastic IP: ${availableEIP.AllocationId}`);
688
+ console.log(
689
+ `Found available Elastic IP: ${availableEIP.AllocationId}`
690
+ );
566
691
  return availableEIP;
567
692
  }
568
-
693
+
569
694
  // Check for EIPs tagged for Frigg
570
- const friggEIP = response.Addresses.find(eip =>
571
- eip.Tags && eip.Tags.some(tag =>
572
- tag.Key === 'Name' && tag.Value.includes('frigg')
573
- )
695
+ const friggEIP = response.Addresses.find(
696
+ (eip) =>
697
+ eip.Tags &&
698
+ eip.Tags.some(
699
+ (tag) =>
700
+ tag.Key === 'Name' &&
701
+ tag.Value.includes('frigg')
702
+ )
574
703
  );
575
-
704
+
576
705
  if (friggEIP) {
577
- console.log(`Found Frigg-tagged Elastic IP: ${friggEIP.AllocationId}`);
706
+ console.log(
707
+ `Found Frigg-tagged Elastic IP: ${friggEIP.AllocationId}`
708
+ );
578
709
  return friggEIP;
579
710
  }
580
711
  }
581
-
712
+
582
713
  return null;
583
714
  } catch (error) {
584
715
  console.warn('Error finding available Elastic IP:', error.message);
@@ -591,6 +722,7 @@ class AWSDiscovery {
591
722
  * @returns {Promise<string|null>} KMS key ARN or null if no key found
592
723
  */
593
724
  async findDefaultKmsKey() {
725
+ console.log('KMS Discovery Starting...');
594
726
  try {
595
727
  // Log AWS account and region info for verification
596
728
  console.log(`[KMS Discovery] Running in region: ${this.region}`);
@@ -598,7 +730,10 @@ class AWSDiscovery {
598
730
  const accountId = await this.getAccountId();
599
731
  console.log(`[KMS Discovery] AWS Account ID: ${accountId}`);
600
732
  } catch (error) {
601
- console.warn('[KMS Discovery] Could not retrieve account ID:', error.message);
733
+ console.warn(
734
+ '[KMS Discovery] Could not retrieve account ID:',
735
+ error.message
736
+ );
602
737
  }
603
738
 
604
739
  const command = new ListKeysCommand({});
@@ -609,7 +744,9 @@ class AWSDiscovery {
609
744
  return null;
610
745
  }
611
746
 
612
- console.log(`[KMS Discovery] Found ${response.Keys.length} total keys in account`);
747
+ console.log(
748
+ `[KMS Discovery] Found ${response.Keys.length} total keys in account`
749
+ );
613
750
  let keysExamined = 0;
614
751
  let customerManagedKeys = 0;
615
752
  let enabledKeys = 0;
@@ -618,8 +755,12 @@ class AWSDiscovery {
618
755
  // Look for customer managed keys first
619
756
  for (const key of response.Keys) {
620
757
  try {
621
- const describeCommand = new DescribeKeyCommand({ KeyId: key.KeyId });
622
- const keyDetails = await this.kmsClient.send(describeCommand);
758
+ const describeCommand = new DescribeKeyCommand({
759
+ KeyId: key.KeyId,
760
+ });
761
+ const keyDetails = await this.kmsClient.send(
762
+ describeCommand
763
+ );
623
764
  keysExamined++;
624
765
 
625
766
  if (keyDetails.KeyMetadata) {
@@ -630,8 +771,10 @@ class AWSDiscovery {
630
771
  KeyManager: metadata.KeyManager,
631
772
  KeyState: metadata.KeyState,
632
773
  Enabled: metadata.Enabled,
633
- DeletionDate: metadata.DeletionDate || 'Not scheduled for deletion',
634
- Arn: metadata.Arn
774
+ DeletionDate:
775
+ metadata.DeletionDate ||
776
+ 'Not scheduled for deletion',
777
+ Arn: metadata.Arn,
635
778
  });
636
779
 
637
780
  if (metadata.KeyManager === 'CUSTOMER') {
@@ -639,28 +782,43 @@ class AWSDiscovery {
639
782
 
640
783
  if (metadata.KeyState === 'Enabled') {
641
784
  enabledKeys++;
642
- } else if (metadata.KeyState === 'PendingDeletion') {
785
+ } else if (
786
+ metadata.KeyState === 'PendingDeletion'
787
+ ) {
643
788
  pendingDeletionKeys++;
644
- console.warn(`[KMS Discovery] Skipping key ${key.KeyId} - State: PendingDeletion, DeletionDate: ${metadata.DeletionDate}`);
789
+ console.warn(
790
+ `[KMS Discovery] Skipping key ${key.KeyId} - State: PendingDeletion, DeletionDate: ${metadata.DeletionDate}`
791
+ );
645
792
  }
646
793
 
647
794
  // Explicitly check for enabled state AND absence of deletion
648
- if (metadata.KeyManager === 'CUSTOMER' &&
795
+ if (
796
+ metadata.KeyManager === 'CUSTOMER' &&
649
797
  metadata.KeyState === 'Enabled' &&
650
- !metadata.DeletionDate) {
651
- console.log(`[KMS Discovery] Found eligible customer managed KMS key: ${metadata.Arn}`);
798
+ !metadata.DeletionDate
799
+ ) {
800
+ console.log(
801
+ `[KMS Discovery] Found eligible customer managed KMS key: ${metadata.Arn}`
802
+ );
652
803
  return metadata.Arn;
653
- } else if (metadata.KeyManager === 'CUSTOMER' &&
654
- metadata.KeyState === 'Enabled' &&
655
- metadata.DeletionDate) {
804
+ } else if (
805
+ metadata.KeyManager === 'CUSTOMER' &&
806
+ metadata.KeyState === 'Enabled' &&
807
+ metadata.DeletionDate
808
+ ) {
656
809
  // This shouldn't happen according to AWS docs, but log it if it does
657
- console.error(`[KMS Discovery] WARNING: Key ${key.KeyId} has KeyState='Enabled' but DeletionDate is set: ${metadata.DeletionDate}`);
810
+ console.error(
811
+ `[KMS Discovery] WARNING: Key ${key.KeyId} has KeyState='Enabled' but DeletionDate is set: ${metadata.DeletionDate}`
812
+ );
658
813
  }
659
814
  }
660
815
  }
661
816
  } catch (error) {
662
817
  // Continue to next key if we can't describe this one
663
- console.warn(`[KMS Discovery] Could not describe key ${key.KeyId}:`, error.message);
818
+ console.warn(
819
+ `[KMS Discovery] Could not describe key ${key.KeyId}:`,
820
+ error.message
821
+ );
664
822
  continue;
665
823
  }
666
824
  }
@@ -671,20 +829,29 @@ class AWSDiscovery {
671
829
  keysExamined: keysExamined,
672
830
  customerManagedKeys: customerManagedKeys,
673
831
  enabledKeys: enabledKeys,
674
- pendingDeletionKeys: pendingDeletionKeys
832
+ pendingDeletionKeys: pendingDeletionKeys,
675
833
  });
676
834
 
677
835
  if (customerManagedKeys === 0) {
678
- console.log('[KMS Discovery] No customer managed KMS keys found in account');
836
+ console.log(
837
+ '[KMS Discovery] No customer managed KMS keys found in account'
838
+ );
679
839
  } else if (enabledKeys === 0) {
680
- console.warn('[KMS Discovery] Found customer managed keys but none are in Enabled state');
840
+ console.warn(
841
+ '[KMS Discovery] Found customer managed keys but none are in Enabled state'
842
+ );
681
843
  } else {
682
- console.warn('[KMS Discovery] Found enabled customer managed keys but none met all criteria');
844
+ console.warn(
845
+ '[KMS Discovery] Found enabled customer managed keys but none met all criteria'
846
+ );
683
847
  }
684
848
 
685
849
  return null;
686
850
  } catch (error) {
687
- console.error('[KMS Discovery] Error finding default KMS key:', error);
851
+ console.error(
852
+ '[KMS Discovery] Error finding default KMS key:',
853
+ error
854
+ );
688
855
  return null;
689
856
  }
690
857
  }
@@ -700,23 +867,26 @@ class AWSDiscovery {
700
867
  natGateways: [],
701
868
  elasticIps: [],
702
869
  subnets: [],
703
- routeTables: []
870
+ routeTables: [],
704
871
  };
705
872
 
706
873
  // Find NAT Gateways with Frigg tags
707
874
  const natCommand = new DescribeNatGatewaysCommand({
708
875
  Filter: [
709
876
  { Name: 'vpc-id', Values: [vpcId] },
710
- { Name: 'state', Values: ['available'] }
711
- ]
877
+ { Name: 'state', Values: ['available'] },
878
+ ],
712
879
  });
713
880
  const natResponse = await this.ec2Client.send(natCommand);
714
881
 
715
882
  if (natResponse.NatGateways) {
716
- resources.natGateways = natResponse.NatGateways.filter(nat =>
717
- nat.Tags && nat.Tags.some(tag =>
718
- tag.Key === 'ManagedBy' && tag.Value === 'Frigg'
719
- )
883
+ resources.natGateways = natResponse.NatGateways.filter(
884
+ (nat) =>
885
+ nat.Tags &&
886
+ nat.Tags.some(
887
+ (tag) =>
888
+ tag.Key === 'ManagedBy' && tag.Value === 'Frigg'
889
+ )
720
890
  );
721
891
  }
722
892
 
@@ -725,26 +895,30 @@ class AWSDiscovery {
725
895
  const eipResponse = await this.ec2Client.send(eipCommand);
726
896
 
727
897
  if (eipResponse.Addresses) {
728
- resources.elasticIps = eipResponse.Addresses.filter(eip =>
729
- eip.Tags && eip.Tags.some(tag =>
730
- tag.Key === 'ManagedBy' && tag.Value === 'Frigg'
731
- )
898
+ resources.elasticIps = eipResponse.Addresses.filter(
899
+ (eip) =>
900
+ eip.Tags &&
901
+ eip.Tags.some(
902
+ (tag) =>
903
+ tag.Key === 'ManagedBy' && tag.Value === 'Frigg'
904
+ )
732
905
  );
733
906
  }
734
907
 
735
908
  // Find Route Tables with Frigg tags
736
909
  const rtCommand = new DescribeRouteTablesCommand({
737
- Filters: [
738
- { Name: 'vpc-id', Values: [vpcId] }
739
- ]
910
+ Filters: [{ Name: 'vpc-id', Values: [vpcId] }],
740
911
  });
741
912
  const rtResponse = await this.ec2Client.send(rtCommand);
742
913
 
743
914
  if (rtResponse.RouteTables) {
744
- resources.routeTables = rtResponse.RouteTables.filter(rt =>
745
- rt.Tags && rt.Tags.some(tag =>
746
- tag.Key === 'ManagedBy' && tag.Value === 'Frigg'
747
- )
915
+ resources.routeTables = rtResponse.RouteTables.filter(
916
+ (rt) =>
917
+ rt.Tags &&
918
+ rt.Tags.some(
919
+ (tag) =>
920
+ tag.Key === 'ManagedBy' && tag.Value === 'Frigg'
921
+ )
748
922
  );
749
923
  }
750
924
 
@@ -755,7 +929,7 @@ class AWSDiscovery {
755
929
  natGateways: [],
756
930
  elasticIps: [],
757
931
  subnets: [],
758
- routeTables: []
932
+ routeTables: [],
759
933
  };
760
934
  }
761
935
  }
@@ -771,15 +945,15 @@ class AWSDiscovery {
771
945
  natGatewaysInPrivateSubnets: [],
772
946
  orphanedElasticIps: [],
773
947
  misconfiguredRouteTables: [],
774
- privateSubnetsWithoutNatRoute: []
948
+ privateSubnetsWithoutNatRoute: [],
775
949
  };
776
950
 
777
951
  // Find NAT Gateways in private subnets
778
952
  const natCommand = new DescribeNatGatewaysCommand({
779
953
  Filter: [
780
954
  { Name: 'vpc-id', Values: [vpcId] },
781
- { Name: 'state', Values: ['available'] }
782
- ]
955
+ { Name: 'state', Values: ['available'] },
956
+ ],
783
957
  });
784
958
  const natResponse = await this.ec2Client.send(natCommand);
785
959
 
@@ -790,7 +964,7 @@ class AWSDiscovery {
790
964
  misconfigurations.natGatewaysInPrivateSubnets.push({
791
965
  natGatewayId: nat.NatGatewayId,
792
966
  subnetId: nat.SubnetId,
793
- tags: nat.Tags
967
+ tags: nat.Tags,
794
968
  });
795
969
  }
796
970
  }
@@ -802,16 +976,24 @@ class AWSDiscovery {
802
976
 
803
977
  if (eipResponse.Addresses) {
804
978
  for (const eip of eipResponse.Addresses) {
805
- if (!eip.InstanceId && !eip.NetworkInterfaceId && !eip.AssociationId) {
979
+ if (
980
+ !eip.InstanceId &&
981
+ !eip.NetworkInterfaceId &&
982
+ !eip.AssociationId
983
+ ) {
806
984
  // Check if it's Frigg-managed
807
- const isFriggManaged = eip.Tags && eip.Tags.some(tag =>
808
- tag.Key === 'ManagedBy' && tag.Value === 'Frigg'
809
- );
985
+ const isFriggManaged =
986
+ eip.Tags &&
987
+ eip.Tags.some(
988
+ (tag) =>
989
+ tag.Key === 'ManagedBy' &&
990
+ tag.Value === 'Frigg'
991
+ );
810
992
  if (isFriggManaged) {
811
993
  misconfigurations.orphanedElasticIps.push({
812
994
  allocationId: eip.AllocationId,
813
995
  publicIp: eip.PublicIp,
814
- tags: eip.Tags
996
+ tags: eip.Tags,
815
997
  });
816
998
  }
817
999
  }
@@ -827,15 +1009,20 @@ class AWSDiscovery {
827
1009
 
828
1010
  // Find route table for this subnet
829
1011
  for (const rt of routeTables) {
830
- const isAssociated = rt.Associations && rt.Associations.some(
831
- assoc => assoc.SubnetId === subnet.SubnetId
832
- );
1012
+ const isAssociated =
1013
+ rt.Associations &&
1014
+ rt.Associations.some(
1015
+ (assoc) => assoc.SubnetId === subnet.SubnetId
1016
+ );
833
1017
 
834
1018
  if (isAssociated) {
835
- hasNatRoute = rt.Routes && rt.Routes.some(
836
- route => route.NatGatewayId &&
1019
+ hasNatRoute =
1020
+ rt.Routes &&
1021
+ rt.Routes.some(
1022
+ (route) =>
1023
+ route.NatGatewayId &&
837
1024
  route.DestinationCidrBlock === '0.0.0.0/0'
838
- );
1025
+ );
839
1026
  break;
840
1027
  }
841
1028
  }
@@ -843,7 +1030,7 @@ class AWSDiscovery {
843
1030
  if (!hasNatRoute) {
844
1031
  misconfigurations.privateSubnetsWithoutNatRoute.push({
845
1032
  subnetId: subnet.SubnetId,
846
- availabilityZone: subnet.AvailabilityZone
1033
+ availabilityZone: subnet.AvailabilityZone,
847
1034
  });
848
1035
  }
849
1036
  }
@@ -855,7 +1042,7 @@ class AWSDiscovery {
855
1042
  natGatewaysInPrivateSubnets: [],
856
1043
  orphanedElasticIps: [],
857
1044
  misconfiguredRouteTables: [],
858
- privateSubnetsWithoutNatRoute: []
1045
+ privateSubnetsWithoutNatRoute: [],
859
1046
  };
860
1047
  }
861
1048
  }
@@ -872,8 +1059,12 @@ class AWSDiscovery {
872
1059
  recommendations.push({
873
1060
  severity: 'critical',
874
1061
  issue: 'NAT Gateway in private subnet',
875
- recommendation: 'Recreate NAT Gateway in public subnet or fix route tables',
876
- affectedResources: misconfigurations.natGatewaysInPrivateSubnets.map(n => n.natGatewayId)
1062
+ recommendation:
1063
+ 'Recreate NAT Gateway in public subnet or fix route tables',
1064
+ affectedResources:
1065
+ misconfigurations.natGatewaysInPrivateSubnets.map(
1066
+ (n) => n.natGatewayId
1067
+ ),
877
1068
  });
878
1069
  }
879
1070
 
@@ -882,7 +1073,9 @@ class AWSDiscovery {
882
1073
  severity: 'warning',
883
1074
  issue: 'Orphaned Elastic IPs',
884
1075
  recommendation: 'Release unused Elastic IPs to avoid charges',
885
- affectedResources: misconfigurations.orphanedElasticIps.map(e => e.allocationId)
1076
+ affectedResources: misconfigurations.orphanedElasticIps.map(
1077
+ (e) => e.allocationId
1078
+ ),
886
1079
  });
887
1080
  }
888
1081
 
@@ -890,8 +1083,12 @@ class AWSDiscovery {
890
1083
  recommendations.push({
891
1084
  severity: 'critical',
892
1085
  issue: 'Private subnets without NAT route',
893
- recommendation: 'Add NAT Gateway route to private subnet route tables',
894
- affectedResources: misconfigurations.privateSubnetsWithoutNatRoute.map(s => s.subnetId)
1086
+ recommendation:
1087
+ 'Add NAT Gateway route to private subnet route tables',
1088
+ affectedResources:
1089
+ misconfigurations.privateSubnetsWithoutNatRoute.map(
1090
+ (s) => s.subnetId
1091
+ ),
895
1092
  });
896
1093
  }
897
1094
 
@@ -918,7 +1115,9 @@ class AWSDiscovery {
918
1115
  */
919
1116
  async discoverResources(options = {}) {
920
1117
  try {
921
- console.log('\nšŸš€ Discovering AWS resources for Frigg deployment...');
1118
+ console.log(
1119
+ '\nšŸš€ Discovering AWS resources for Frigg deployment...'
1120
+ );
922
1121
  console.log('═'.repeat(60));
923
1122
 
924
1123
  const vpc = await this.findDefaultVpc();
@@ -927,17 +1126,30 @@ class AWSDiscovery {
927
1126
  // Enable auto-convert if selfHeal is enabled
928
1127
  const autoConvert = options.selfHeal || false;
929
1128
 
930
- const privateSubnets = await this.findPrivateSubnets(vpc.VpcId, autoConvert);
931
- console.log(`\nāœ… Selected subnets for Lambda: ${privateSubnets.map(s => s.SubnetId).join(', ')}`);
1129
+ const privateSubnets = await this.findPrivateSubnets(
1130
+ vpc.VpcId,
1131
+ autoConvert
1132
+ );
1133
+ console.log(
1134
+ `\nāœ… Selected subnets for Lambda: ${privateSubnets
1135
+ .map((s) => s.SubnetId)
1136
+ .join(', ')}`
1137
+ );
932
1138
 
933
1139
  const publicSubnet = await this.findPublicSubnets(vpc.VpcId);
934
1140
  if (publicSubnet) {
935
- console.log(`\nāœ… Found public subnet for NAT Gateway: ${publicSubnet.SubnetId}`);
1141
+ console.log(
1142
+ `\nāœ… Found public subnet for NAT Gateway: ${publicSubnet.SubnetId}`
1143
+ );
936
1144
  } else {
937
- console.log(`\nāš ļø No public subnet found - NAT Gateway creation may fail`);
1145
+ console.log(
1146
+ `\nāš ļø No public subnet found - NAT Gateway creation may fail`
1147
+ );
938
1148
  }
939
1149
 
940
- const securityGroup = await this.findDefaultSecurityGroup(vpc.VpcId);
1150
+ const securityGroup = await this.findDefaultSecurityGroup(
1151
+ vpc.VpcId
1152
+ );
941
1153
  console.log(`\nāœ… Found security group: ${securityGroup.GroupId}`);
942
1154
 
943
1155
  const routeTable = await this.findPrivateRouteTable(vpc.VpcId);
@@ -949,9 +1161,11 @@ class AWSDiscovery {
949
1161
  } else {
950
1162
  console.log('ā„¹ļø No KMS key found');
951
1163
  }
952
-
1164
+
953
1165
  // Try to find existing NAT Gateway
954
- const existingNatGateway = await this.findExistingNatGateway(vpc.VpcId);
1166
+ const existingNatGateway = await this.findExistingNatGateway(
1167
+ vpc.VpcId
1168
+ );
955
1169
  let natGatewayId = null;
956
1170
  let elasticIpAllocationId = null;
957
1171
  let natGatewayInPrivateSubnet = false;
@@ -959,11 +1173,16 @@ class AWSDiscovery {
959
1173
  if (existingNatGateway) {
960
1174
  natGatewayId = existingNatGateway.NatGatewayId;
961
1175
  // Check if NAT Gateway is in a private subnet (from our detection)
962
- natGatewayInPrivateSubnet = existingNatGateway._isInPrivateSubnet || false;
1176
+ natGatewayInPrivateSubnet =
1177
+ existingNatGateway._isInPrivateSubnet || false;
963
1178
 
964
1179
  // Get the EIP allocation ID from the NAT Gateway
965
- if (existingNatGateway.NatGatewayAddresses && existingNatGateway.NatGatewayAddresses.length > 0) {
966
- elasticIpAllocationId = existingNatGateway.NatGatewayAddresses[0].AllocationId;
1180
+ if (
1181
+ existingNatGateway.NatGatewayAddresses &&
1182
+ existingNatGateway.NatGatewayAddresses.length > 0
1183
+ ) {
1184
+ elasticIpAllocationId =
1185
+ existingNatGateway.NatGatewayAddresses[0].AllocationId;
967
1186
  }
968
1187
  } else {
969
1188
  // If no NAT Gateway exists, check for available EIP
@@ -974,36 +1193,58 @@ class AWSDiscovery {
974
1193
  }
975
1194
 
976
1195
  // Check if the "private" subnets are actually public
977
- const subnet1IsActuallyPrivate = privateSubnets[0] ?
978
- await this.isSubnetPrivate(privateSubnets[0].SubnetId) : false;
979
- const subnet2IsActuallyPrivate = privateSubnets[1] ?
980
- await this.isSubnetPrivate(privateSubnets[1].SubnetId) :
981
- subnet1IsActuallyPrivate;
1196
+ const subnet1IsActuallyPrivate = privateSubnets[0]
1197
+ ? await this.isSubnetPrivate(privateSubnets[0].SubnetId)
1198
+ : false;
1199
+ const subnet2IsActuallyPrivate = privateSubnets[1]
1200
+ ? await this.isSubnetPrivate(privateSubnets[1].SubnetId)
1201
+ : subnet1IsActuallyPrivate;
982
1202
 
983
1203
  const subnetStatus = {
984
- requiresConversion: !subnet1IsActuallyPrivate || !subnet2IsActuallyPrivate,
1204
+ requiresConversion:
1205
+ !subnet1IsActuallyPrivate || !subnet2IsActuallyPrivate,
985
1206
  subnet1NeedsConversion: !subnet1IsActuallyPrivate,
986
- subnet2NeedsConversion: !subnet2IsActuallyPrivate
1207
+ subnet2NeedsConversion: !subnet2IsActuallyPrivate,
987
1208
  };
988
1209
 
989
1210
  if (subnetStatus.requiresConversion) {
990
1211
  console.log(`\nāš ļø SUBNET CONFIGURATION WARNING:`);
991
1212
  if (subnetStatus.subnet1NeedsConversion && privateSubnets[0]) {
992
- console.log(` - Subnet ${privateSubnets[0].SubnetId} is currently PUBLIC but will be used for Lambda`);
1213
+ console.log(
1214
+ ` - Subnet ${privateSubnets[0].SubnetId} is currently PUBLIC but will be used for Lambda`
1215
+ );
993
1216
  }
994
1217
  if (subnetStatus.subnet2NeedsConversion && privateSubnets[1]) {
995
- console.log(` - Subnet ${privateSubnets[1].SubnetId} is currently PUBLIC but will be used for Lambda`);
1218
+ console.log(
1219
+ ` - Subnet ${privateSubnets[1].SubnetId} is currently PUBLIC but will be used for Lambda`
1220
+ );
996
1221
  }
997
- console.log(` šŸ’” Enable selfHeal: true to automatically fix this`);
1222
+ console.log(
1223
+ ` šŸ’” Enable selfHeal: true to automatically fix this`
1224
+ );
998
1225
  }
999
1226
 
1000
1227
  console.log(`\n${'═'.repeat(60)}`);
1001
1228
  console.log('šŸ“‹ Discovery Summary:');
1002
1229
  console.log(` VPC: ${vpc.VpcId}`);
1003
- console.log(` Lambda Subnets: ${privateSubnets.map(s => s.SubnetId).join(', ')}`);
1004
- console.log(` NAT Subnet: ${publicSubnet?.SubnetId || 'None (needs creation)'}`);
1005
- console.log(` NAT Gateway: ${natGatewayId || 'None (will be created)'}`);
1006
- console.log(` Elastic IP: ${elasticIpAllocationId || 'None (will be allocated)'}`);
1230
+ console.log(
1231
+ ` Lambda Subnets: ${privateSubnets
1232
+ .map((s) => s.SubnetId)
1233
+ .join(', ')}`
1234
+ );
1235
+ console.log(
1236
+ ` NAT Subnet: ${
1237
+ publicSubnet?.SubnetId || 'None (needs creation)'
1238
+ }`
1239
+ );
1240
+ console.log(
1241
+ ` NAT Gateway: ${natGatewayId || 'None (will be created)'}`
1242
+ );
1243
+ console.log(
1244
+ ` Elastic IP: ${
1245
+ elasticIpAllocationId || 'None (will be allocated)'
1246
+ }`
1247
+ );
1007
1248
  if (subnetStatus.requiresConversion) {
1008
1249
  console.log(` āš ļø Subnet Conversion Required: Yes`);
1009
1250
  }
@@ -1013,7 +1254,8 @@ class AWSDiscovery {
1013
1254
  defaultVpcId: vpc.VpcId,
1014
1255
  defaultSecurityGroupId: securityGroup.GroupId,
1015
1256
  privateSubnetId1: privateSubnets[0]?.SubnetId,
1016
- privateSubnetId2: privateSubnets[1]?.SubnetId || privateSubnets[0]?.SubnetId,
1257
+ privateSubnetId2:
1258
+ privateSubnets[1]?.SubnetId || privateSubnets[0]?.SubnetId,
1017
1259
  publicSubnetId: publicSubnet?.SubnetId || null, // May be null if no public subnet exists
1018
1260
  privateRouteTableId: routeTable.RouteTableId,
1019
1261
  defaultKmsKeyId: kmsKeyArn,
@@ -1023,14 +1265,20 @@ class AWSDiscovery {
1023
1265
  subnetConversionRequired: subnetStatus.requiresConversion,
1024
1266
  privateSubnetsWithWrongRoutes: (() => {
1025
1267
  const wrongRoutes = [];
1026
- if (subnetStatus.subnet1NeedsConversion && privateSubnets[0]) {
1268
+ if (
1269
+ subnetStatus.subnet1NeedsConversion &&
1270
+ privateSubnets[0]
1271
+ ) {
1027
1272
  wrongRoutes.push(privateSubnets[0].SubnetId);
1028
1273
  }
1029
- if (subnetStatus.subnet2NeedsConversion && privateSubnets[1]) {
1274
+ if (
1275
+ subnetStatus.subnet2NeedsConversion &&
1276
+ privateSubnets[1]
1277
+ ) {
1030
1278
  wrongRoutes.push(privateSubnets[1].SubnetId);
1031
1279
  }
1032
1280
  return wrongRoutes;
1033
- })()
1281
+ })(),
1034
1282
  };
1035
1283
  } catch (error) {
1036
1284
  console.error('Error discovering AWS resources:', error);
@@ -1049,19 +1297,24 @@ class AWSDiscovery {
1049
1297
  Filters: [
1050
1298
  {
1051
1299
  Name: 'attachment.vpc-id',
1052
- Values: [vpcId]
1300
+ Values: [vpcId],
1053
1301
  },
1054
1302
  {
1055
1303
  Name: 'attachment.state',
1056
- Values: ['available']
1057
- }
1058
- ]
1304
+ Values: ['available'],
1305
+ },
1306
+ ],
1059
1307
  });
1060
1308
 
1061
1309
  const response = await this.ec2Client.send(command);
1062
1310
 
1063
- if (response.InternetGateways && response.InternetGateways.length > 0) {
1064
- console.log(`Found existing Internet Gateway: ${response.InternetGateways[0].InternetGatewayId}`);
1311
+ if (
1312
+ response.InternetGateways &&
1313
+ response.InternetGateways.length > 0
1314
+ ) {
1315
+ console.log(
1316
+ `Found existing Internet Gateway: ${response.InternetGateways[0].InternetGatewayId}`
1317
+ );
1065
1318
  return response.InternetGateways[0];
1066
1319
  }
1067
1320
 
@@ -1085,28 +1338,28 @@ class AWSDiscovery {
1085
1338
  elasticIps: [],
1086
1339
  routeTables: [],
1087
1340
  subnets: [],
1088
- securityGroups: []
1341
+ securityGroups: [],
1089
1342
  };
1090
1343
 
1091
1344
  // Common filter for Frigg-managed resources
1092
1345
  const friggFilters = [
1093
1346
  {
1094
1347
  Name: 'tag:ManagedBy',
1095
- Values: ['Frigg']
1096
- }
1348
+ Values: ['Frigg'],
1349
+ },
1097
1350
  ];
1098
1351
 
1099
1352
  if (serviceName) {
1100
1353
  friggFilters.push({
1101
1354
  Name: 'tag:Service',
1102
- Values: [serviceName]
1355
+ Values: [serviceName],
1103
1356
  });
1104
1357
  }
1105
1358
 
1106
1359
  if (stage) {
1107
1360
  friggFilters.push({
1108
1361
  Name: 'tag:Stage',
1109
- Values: [stage]
1362
+ Values: [stage],
1110
1363
  });
1111
1364
  }
1112
1365
 
@@ -1117,9 +1370,9 @@ class AWSDiscovery {
1117
1370
  ...friggFilters,
1118
1371
  {
1119
1372
  Name: 'state',
1120
- Values: ['available']
1121
- }
1122
- ]
1373
+ Values: ['available'],
1374
+ },
1375
+ ],
1123
1376
  });
1124
1377
  const natResponse = await this.ec2Client.send(natCommand);
1125
1378
  results.natGateways = natResponse.NatGateways || [];
@@ -1130,7 +1383,7 @@ class AWSDiscovery {
1130
1383
  // Find Elastic IPs
1131
1384
  try {
1132
1385
  const eipCommand = new DescribeAddressesCommand({
1133
- Filters: friggFilters
1386
+ Filters: friggFilters,
1134
1387
  });
1135
1388
  const eipResponse = await this.ec2Client.send(eipCommand);
1136
1389
  results.elasticIps = eipResponse.Addresses || [];
@@ -1141,7 +1394,7 @@ class AWSDiscovery {
1141
1394
  // Find Route Tables
1142
1395
  try {
1143
1396
  const rtCommand = new DescribeRouteTablesCommand({
1144
- Filters: friggFilters
1397
+ Filters: friggFilters,
1145
1398
  });
1146
1399
  const rtResponse = await this.ec2Client.send(rtCommand);
1147
1400
  results.routeTables = rtResponse.RouteTables || [];
@@ -1152,7 +1405,7 @@ class AWSDiscovery {
1152
1405
  // Find Subnets
1153
1406
  try {
1154
1407
  const subnetCommand = new DescribeSubnetsCommand({
1155
- Filters: friggFilters
1408
+ Filters: friggFilters,
1156
1409
  });
1157
1410
  const subnetResponse = await this.ec2Client.send(subnetCommand);
1158
1411
  results.subnets = subnetResponse.Subnets || [];
@@ -1163,12 +1416,15 @@ class AWSDiscovery {
1163
1416
  // Find Security Groups
1164
1417
  try {
1165
1418
  const sgCommand = new DescribeSecurityGroupsCommand({
1166
- Filters: friggFilters
1419
+ Filters: friggFilters,
1167
1420
  });
1168
1421
  const sgResponse = await this.ec2Client.send(sgCommand);
1169
1422
  results.securityGroups = sgResponse.SecurityGroups || [];
1170
1423
  } catch (err) {
1171
- console.warn('Error finding Frigg Security Groups:', err.message);
1424
+ console.warn(
1425
+ 'Error finding Frigg Security Groups:',
1426
+ err.message
1427
+ );
1172
1428
  }
1173
1429
 
1174
1430
  console.log('Found Frigg-managed resources:', {
@@ -1176,7 +1432,7 @@ class AWSDiscovery {
1176
1432
  elasticIps: results.elasticIps.length,
1177
1433
  routeTables: results.routeTables.length,
1178
1434
  subnets: results.subnets.length,
1179
- securityGroups: results.securityGroups.length
1435
+ securityGroups: results.securityGroups.length,
1180
1436
  });
1181
1437
 
1182
1438
  return results;
@@ -1187,10 +1443,10 @@ class AWSDiscovery {
1187
1443
  elasticIps: [],
1188
1444
  routeTables: [],
1189
1445
  subnets: [],
1190
- securityGroups: []
1446
+ securityGroups: [],
1191
1447
  };
1192
1448
  }
1193
1449
  }
1194
1450
  }
1195
1451
 
1196
- module.exports = { AWSDiscovery };
1452
+ module.exports = { AWSDiscovery };