@friggframework/devtools 2.0.0--canary.461.4116d1e.0 → 2.0.0--canary.461.39e4094.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.
- package/infrastructure/domains/networking/vpc-builder.js +1 -1
- package/infrastructure/domains/shared/cloudformation-discovery.js +67 -12
- package/infrastructure/domains/shared/cloudformation-discovery.test.js +161 -0
- package/infrastructure/domains/shared/providers/aws-provider-adapter.js +21 -1
- package/infrastructure/domains/shared/resource-discovery.js +17 -9
- package/package.json +6 -6
|
@@ -989,7 +989,7 @@ class VpcBuilder extends InfrastructureBuilder {
|
|
|
989
989
|
}
|
|
990
990
|
|
|
991
991
|
// VPC Endpoint Security Group (only if KMS, Secrets Manager, or SQS are not stack-managed and missing)
|
|
992
|
-
const needsSecurityGroup =
|
|
992
|
+
const needsSecurityGroup =
|
|
993
993
|
(!stackManagedEndpoints.kms && !existingEndpoints.kms && appDefinition.encryption?.fieldLevelEncryptionMethod === 'kms') ||
|
|
994
994
|
(!stackManagedEndpoints.secretsManager && !existingEndpoints.secretsManager) ||
|
|
995
995
|
(!stackManagedEndpoints.sqs && !existingEndpoints.sqs);
|
|
@@ -14,8 +14,10 @@
|
|
|
14
14
|
*/
|
|
15
15
|
|
|
16
16
|
class CloudFormationDiscovery {
|
|
17
|
-
constructor(provider) {
|
|
17
|
+
constructor(provider, config = {}) {
|
|
18
18
|
this.provider = provider;
|
|
19
|
+
this.serviceName = config.serviceName;
|
|
20
|
+
this.stage = config.stage;
|
|
19
21
|
}
|
|
20
22
|
|
|
21
23
|
/**
|
|
@@ -41,9 +43,8 @@ class CloudFormationDiscovery {
|
|
|
41
43
|
}
|
|
42
44
|
|
|
43
45
|
// Extract from resources (now async to query AWS for details)
|
|
44
|
-
if
|
|
45
|
-
|
|
46
|
-
}
|
|
46
|
+
// Always call this even if resources is empty, as it may query AWS for resources
|
|
47
|
+
await this._extractFromResources(resources || [], discovered);
|
|
47
48
|
|
|
48
49
|
return discovered;
|
|
49
50
|
} catch (error) {
|
|
@@ -121,18 +122,21 @@ class CloudFormationDiscovery {
|
|
|
121
122
|
if (LogicalResourceId === 'FriggLambdaSecurityGroup' && ResourceType === 'AWS::EC2::SecurityGroup') {
|
|
122
123
|
discovered.securityGroupId = PhysicalResourceId;
|
|
123
124
|
console.log(` ✓ Found security group in stack: ${PhysicalResourceId}`);
|
|
124
|
-
|
|
125
|
-
|
|
125
|
+
|
|
126
|
+
// Query security group to get VPC ID (required because SG resource doesn't include VPC ID)
|
|
127
|
+
if (this.provider && this.provider.getEC2Client && !discovered.defaultVpcId) {
|
|
126
128
|
try {
|
|
127
|
-
console.log(` Querying
|
|
129
|
+
console.log(` Querying EC2 to get VPC ID from security group...`);
|
|
128
130
|
const { DescribeSecurityGroupsCommand } = require('@aws-sdk/client-ec2');
|
|
129
|
-
const
|
|
131
|
+
const ec2Client = this.provider.getEC2Client();
|
|
132
|
+
const sgDetails = await ec2Client.send(
|
|
130
133
|
new DescribeSecurityGroupsCommand({
|
|
131
134
|
GroupIds: [PhysicalResourceId]
|
|
132
135
|
})
|
|
133
136
|
);
|
|
137
|
+
|
|
134
138
|
if (sgDetails.SecurityGroups && sgDetails.SecurityGroups.length > 0) {
|
|
135
|
-
discovered.defaultVpcId = sgDetails.SecurityGroups[0].VpcId;
|
|
139
|
+
discovered.defaultVpcId = sgDetails.SecurityGroups[0].VpcId;
|
|
136
140
|
console.log(` ✓ Extracted VPC ID from security group: ${discovered.defaultVpcId}`);
|
|
137
141
|
} else {
|
|
138
142
|
console.warn(` ⚠️ Security group query returned no results`);
|
|
@@ -192,6 +196,12 @@ class CloudFormationDiscovery {
|
|
|
192
196
|
discovered.natGatewayId = PhysicalResourceId;
|
|
193
197
|
}
|
|
194
198
|
|
|
199
|
+
// VPC - direct extraction (primary method)
|
|
200
|
+
if (LogicalResourceId === 'FriggVPC' && ResourceType === 'AWS::EC2::VPC') {
|
|
201
|
+
discovered.defaultVpcId = PhysicalResourceId;
|
|
202
|
+
console.log(` ✓ Found VPC in stack: ${PhysicalResourceId}`);
|
|
203
|
+
}
|
|
204
|
+
|
|
195
205
|
// KMS Key (alternative to output)
|
|
196
206
|
if (LogicalResourceId === 'FriggKMSKey' && ResourceType === 'AWS::KMS::Key') {
|
|
197
207
|
// Note: For KMS, we prefer the ARN from outputs, but this is a fallback
|
|
@@ -200,6 +210,30 @@ class CloudFormationDiscovery {
|
|
|
200
210
|
}
|
|
201
211
|
}
|
|
202
212
|
|
|
213
|
+
// KMS Key Alias - query to get the actual key ARN
|
|
214
|
+
if (LogicalResourceId === 'FriggKMSKeyAlias' && ResourceType === 'AWS::KMS::Alias') {
|
|
215
|
+
discovered.kmsKeyAlias = PhysicalResourceId;
|
|
216
|
+
console.log(` ✓ Found KMS key alias in stack: ${PhysicalResourceId}`);
|
|
217
|
+
|
|
218
|
+
// Query KMS to get the key ARN that this alias points to
|
|
219
|
+
// Always query even if key is already set, to ensure consistency
|
|
220
|
+
if (this.provider && this.provider.describeKmsKey) {
|
|
221
|
+
try {
|
|
222
|
+
console.log(` Querying KMS to get key ARN from alias...`);
|
|
223
|
+
const keyMetadata = await this.provider.describeKmsKey(PhysicalResourceId);
|
|
224
|
+
|
|
225
|
+
if (keyMetadata) {
|
|
226
|
+
discovered.defaultKmsKeyId = keyMetadata.Arn;
|
|
227
|
+
console.log(` ✓ Extracted KMS key ARN from alias: ${discovered.defaultKmsKeyId}`);
|
|
228
|
+
} else {
|
|
229
|
+
console.warn(` ⚠️ KMS key query returned no metadata`);
|
|
230
|
+
}
|
|
231
|
+
} catch (error) {
|
|
232
|
+
console.warn(` ⚠️ Could not get key ARN from alias: ${error.message}`);
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
|
|
203
237
|
// Subnets
|
|
204
238
|
if (LogicalResourceId === 'FriggPrivateSubnet1' && ResourceType === 'AWS::EC2::Subnet') {
|
|
205
239
|
discovered.privateSubnetId1 = PhysicalResourceId;
|
|
@@ -243,7 +277,7 @@ class CloudFormationDiscovery {
|
|
|
243
277
|
}
|
|
244
278
|
|
|
245
279
|
// If we have a VPC ID but no subnet IDs, query EC2 for Frigg-managed subnets
|
|
246
|
-
if (discovered.defaultVpcId && this.provider &&
|
|
280
|
+
if (discovered.defaultVpcId && this.provider &&
|
|
247
281
|
!discovered.privateSubnetId1 && !discovered.publicSubnetId1) {
|
|
248
282
|
try {
|
|
249
283
|
console.log(' Querying EC2 for Frigg-managed subnets...');
|
|
@@ -266,7 +300,7 @@ class CloudFormationDiscovery {
|
|
|
266
300
|
}));
|
|
267
301
|
|
|
268
302
|
// Find private subnets
|
|
269
|
-
const privateSubnets = subnets.filter(s => !s.isPublic).sort((a, b) =>
|
|
303
|
+
const privateSubnets = subnets.filter(s => !s.isPublic).sort((a, b) =>
|
|
270
304
|
a.logicalId?.localeCompare(b.logicalId) || 0
|
|
271
305
|
);
|
|
272
306
|
if (privateSubnets.length >= 1) {
|
|
@@ -277,7 +311,7 @@ class CloudFormationDiscovery {
|
|
|
277
311
|
}
|
|
278
312
|
|
|
279
313
|
// Find public subnets
|
|
280
|
-
const publicSubnets = subnets.filter(s => s.isPublic).sort((a, b) =>
|
|
314
|
+
const publicSubnets = subnets.filter(s => s.isPublic).sort((a, b) =>
|
|
281
315
|
a.logicalId?.localeCompare(b.logicalId) || 0
|
|
282
316
|
);
|
|
283
317
|
if (publicSubnets.length >= 1) {
|
|
@@ -293,6 +327,27 @@ class CloudFormationDiscovery {
|
|
|
293
327
|
console.warn(` ⚠️ Could not query EC2 for subnets: ${error.message}`);
|
|
294
328
|
}
|
|
295
329
|
}
|
|
330
|
+
|
|
331
|
+
// Check for KMS key alias via AWS API if not found in stack resources
|
|
332
|
+
// This handles cases where the alias was created outside CloudFormation
|
|
333
|
+
if (!discovered.defaultKmsKeyId && !discovered.kmsKeyAlias &&
|
|
334
|
+
this.provider && this.provider.describeKmsKey && this.serviceName && this.stage) {
|
|
335
|
+
try {
|
|
336
|
+
const aliasName = `alias/${this.serviceName}-${this.stage}-frigg-kms`;
|
|
337
|
+
console.log(` Querying KMS for alias: ${aliasName}...`);
|
|
338
|
+
|
|
339
|
+
const keyMetadata = await this.provider.describeKmsKey(aliasName);
|
|
340
|
+
|
|
341
|
+
if (keyMetadata) {
|
|
342
|
+
discovered.defaultKmsKeyId = keyMetadata.Arn;
|
|
343
|
+
discovered.kmsKeyAlias = aliasName;
|
|
344
|
+
console.log(` ✓ Found KMS key via alias query: ${discovered.defaultKmsKeyId}`);
|
|
345
|
+
}
|
|
346
|
+
} catch (error) {
|
|
347
|
+
// Alias not found - this is expected if no KMS key exists yet
|
|
348
|
+
console.log(` ℹ No KMS key alias found via AWS API`);
|
|
349
|
+
}
|
|
350
|
+
}
|
|
296
351
|
}
|
|
297
352
|
}
|
|
298
353
|
|
|
@@ -253,6 +253,30 @@ describe('CloudFormationDiscovery', () => {
|
|
|
253
253
|
});
|
|
254
254
|
});
|
|
255
255
|
|
|
256
|
+
it('should extract VPC directly from stack resources', async () => {
|
|
257
|
+
const mockStack = {
|
|
258
|
+
StackName: 'test-stack',
|
|
259
|
+
Outputs: [],
|
|
260
|
+
};
|
|
261
|
+
|
|
262
|
+
const mockResources = [
|
|
263
|
+
{
|
|
264
|
+
LogicalResourceId: 'FriggVPC',
|
|
265
|
+
PhysicalResourceId: 'vpc-037ec55fe87aec1e7',
|
|
266
|
+
ResourceType: 'AWS::EC2::VPC',
|
|
267
|
+
},
|
|
268
|
+
];
|
|
269
|
+
|
|
270
|
+
mockProvider.describeStack.mockResolvedValue(mockStack);
|
|
271
|
+
mockProvider.listStackResources.mockResolvedValue(mockResources);
|
|
272
|
+
|
|
273
|
+
const result = await cfDiscovery.discoverFromStack('test-stack');
|
|
274
|
+
|
|
275
|
+
expect(result).toEqual({
|
|
276
|
+
defaultVpcId: 'vpc-037ec55fe87aec1e7',
|
|
277
|
+
});
|
|
278
|
+
});
|
|
279
|
+
|
|
256
280
|
it('should combine outputs and resources correctly', async () => {
|
|
257
281
|
const mockStack = {
|
|
258
282
|
StackName: 'test-stack',
|
|
@@ -399,6 +423,143 @@ describe('CloudFormationDiscovery', () => {
|
|
|
399
423
|
expect(result.defaultVpcId).toBe('vpc-123');
|
|
400
424
|
expect(result.privateSubnetId1).toBeUndefined();
|
|
401
425
|
});
|
|
426
|
+
|
|
427
|
+
it('should extract KMS key alias from stack resources and query for key ARN', async () => {
|
|
428
|
+
const mockStack = {
|
|
429
|
+
StackName: 'test-stack',
|
|
430
|
+
Outputs: [],
|
|
431
|
+
};
|
|
432
|
+
|
|
433
|
+
const mockResources = [
|
|
434
|
+
{
|
|
435
|
+
LogicalResourceId: 'FriggKMSKeyAlias',
|
|
436
|
+
PhysicalResourceId: 'alias/test-service-dev-frigg-kms',
|
|
437
|
+
ResourceType: 'AWS::KMS::Alias',
|
|
438
|
+
},
|
|
439
|
+
];
|
|
440
|
+
|
|
441
|
+
mockProvider.describeStack.mockResolvedValue(mockStack);
|
|
442
|
+
mockProvider.listStackResources.mockResolvedValue(mockResources);
|
|
443
|
+
mockProvider.describeKmsKey = jest.fn().mockResolvedValue({
|
|
444
|
+
KeyId: 'abc-123',
|
|
445
|
+
Arn: 'arn:aws:kms:us-east-1:123456789:key/abc-123',
|
|
446
|
+
});
|
|
447
|
+
|
|
448
|
+
const result = await cfDiscovery.discoverFromStack('test-stack');
|
|
449
|
+
|
|
450
|
+
expect(result.defaultKmsKeyId).toBe('arn:aws:kms:us-east-1:123456789:key/abc-123');
|
|
451
|
+
expect(result.kmsKeyAlias).toBe('alias/test-service-dev-frigg-kms');
|
|
452
|
+
expect(mockProvider.describeKmsKey).toHaveBeenCalledWith('alias/test-service-dev-frigg-kms');
|
|
453
|
+
});
|
|
454
|
+
|
|
455
|
+
it('should query AWS API for KMS alias when serviceName and stage are provided', async () => {
|
|
456
|
+
const mockStack = {
|
|
457
|
+
StackName: 'test-stack',
|
|
458
|
+
Outputs: [],
|
|
459
|
+
};
|
|
460
|
+
|
|
461
|
+
const mockResources = [];
|
|
462
|
+
|
|
463
|
+
mockProvider.describeStack.mockResolvedValue(mockStack);
|
|
464
|
+
mockProvider.listStackResources.mockResolvedValue(mockResources);
|
|
465
|
+
mockProvider.region = 'us-east-1';
|
|
466
|
+
mockProvider.describeKmsKey = jest.fn().mockResolvedValue({
|
|
467
|
+
KeyId: 'abc-123',
|
|
468
|
+
Arn: 'arn:aws:kms:us-east-1:123456789:key/abc-123',
|
|
469
|
+
});
|
|
470
|
+
|
|
471
|
+
// Pass serviceName and stage to discover alias
|
|
472
|
+
cfDiscovery.serviceName = 'test-service';
|
|
473
|
+
cfDiscovery.stage = 'dev';
|
|
474
|
+
|
|
475
|
+
const result = await cfDiscovery.discoverFromStack('test-stack');
|
|
476
|
+
|
|
477
|
+
expect(result.defaultKmsKeyId).toBe('arn:aws:kms:us-east-1:123456789:key/abc-123');
|
|
478
|
+
expect(result.kmsKeyAlias).toBe('alias/test-service-dev-frigg-kms');
|
|
479
|
+
expect(mockProvider.describeKmsKey).toHaveBeenCalledWith('alias/test-service-dev-frigg-kms');
|
|
480
|
+
});
|
|
481
|
+
|
|
482
|
+
it('should handle KMS alias not found gracefully', async () => {
|
|
483
|
+
const mockStack = {
|
|
484
|
+
StackName: 'test-stack',
|
|
485
|
+
Outputs: [],
|
|
486
|
+
};
|
|
487
|
+
|
|
488
|
+
const mockResources = [];
|
|
489
|
+
|
|
490
|
+
mockProvider.describeStack.mockResolvedValue(mockStack);
|
|
491
|
+
mockProvider.listStackResources.mockResolvedValue(mockResources);
|
|
492
|
+
mockProvider.region = 'us-east-1';
|
|
493
|
+
mockProvider.describeKmsKey = jest.fn().mockRejectedValue(
|
|
494
|
+
new Error('Alias/test-service-dev-frigg-kms is not found')
|
|
495
|
+
);
|
|
496
|
+
|
|
497
|
+
cfDiscovery.serviceName = 'test-service';
|
|
498
|
+
cfDiscovery.stage = 'dev';
|
|
499
|
+
|
|
500
|
+
const result = await cfDiscovery.discoverFromStack('test-stack');
|
|
501
|
+
|
|
502
|
+
expect(result.defaultKmsKeyId).toBeUndefined();
|
|
503
|
+
expect(result.kmsKeyAlias).toBeUndefined();
|
|
504
|
+
});
|
|
505
|
+
|
|
506
|
+
it('should prefer KMS key from stack resources over alias query', async () => {
|
|
507
|
+
const mockStack = {
|
|
508
|
+
StackName: 'test-stack',
|
|
509
|
+
Outputs: [],
|
|
510
|
+
};
|
|
511
|
+
|
|
512
|
+
const mockResources = [
|
|
513
|
+
{
|
|
514
|
+
LogicalResourceId: 'FriggKMSKey',
|
|
515
|
+
PhysicalResourceId: 'arn:aws:kms:us-east-1:123456789:key/xyz-789',
|
|
516
|
+
ResourceType: 'AWS::KMS::Key',
|
|
517
|
+
},
|
|
518
|
+
];
|
|
519
|
+
|
|
520
|
+
mockProvider.describeStack.mockResolvedValue(mockStack);
|
|
521
|
+
mockProvider.listStackResources.mockResolvedValue(mockResources);
|
|
522
|
+
mockProvider.describeKmsKey = jest.fn();
|
|
523
|
+
|
|
524
|
+
const result = await cfDiscovery.discoverFromStack('test-stack');
|
|
525
|
+
|
|
526
|
+
// Should use the key from stack resources, not query for alias
|
|
527
|
+
expect(result.defaultKmsKeyId).toBe('arn:aws:kms:us-east-1:123456789:key/xyz-789');
|
|
528
|
+
expect(mockProvider.describeKmsKey).not.toHaveBeenCalled();
|
|
529
|
+
});
|
|
530
|
+
|
|
531
|
+
it('should use KMS alias from stack resources even if key is also present', async () => {
|
|
532
|
+
const mockStack = {
|
|
533
|
+
StackName: 'test-stack',
|
|
534
|
+
Outputs: [],
|
|
535
|
+
};
|
|
536
|
+
|
|
537
|
+
const mockResources = [
|
|
538
|
+
{
|
|
539
|
+
LogicalResourceId: 'FriggKMSKey',
|
|
540
|
+
PhysicalResourceId: 'arn:aws:kms:us-east-1:123456789:key/xyz-789',
|
|
541
|
+
ResourceType: 'AWS::KMS::Key',
|
|
542
|
+
},
|
|
543
|
+
{
|
|
544
|
+
LogicalResourceId: 'FriggKMSKeyAlias',
|
|
545
|
+
PhysicalResourceId: 'alias/test-service-dev-frigg-kms',
|
|
546
|
+
ResourceType: 'AWS::KMS::Alias',
|
|
547
|
+
},
|
|
548
|
+
];
|
|
549
|
+
|
|
550
|
+
mockProvider.describeStack.mockResolvedValue(mockStack);
|
|
551
|
+
mockProvider.listStackResources.mockResolvedValue(mockResources);
|
|
552
|
+
mockProvider.describeKmsKey = jest.fn().mockResolvedValue({
|
|
553
|
+
KeyId: 'xyz-789',
|
|
554
|
+
Arn: 'arn:aws:kms:us-east-1:123456789:key/xyz-789',
|
|
555
|
+
});
|
|
556
|
+
|
|
557
|
+
const result = await cfDiscovery.discoverFromStack('test-stack');
|
|
558
|
+
|
|
559
|
+
expect(result.defaultKmsKeyId).toBe('arn:aws:kms:us-east-1:123456789:key/xyz-789');
|
|
560
|
+
expect(result.kmsKeyAlias).toBe('alias/test-service-dev-frigg-kms');
|
|
561
|
+
expect(mockProvider.describeKmsKey).toHaveBeenCalledWith('alias/test-service-dev-frigg-kms');
|
|
562
|
+
});
|
|
402
563
|
});
|
|
403
564
|
});
|
|
404
565
|
|
|
@@ -469,9 +469,29 @@ class AWSProviderAdapter extends CloudProviderAdapter {
|
|
|
469
469
|
return result;
|
|
470
470
|
}
|
|
471
471
|
|
|
472
|
+
/**
|
|
473
|
+
* Describe KMS key by key ID or alias
|
|
474
|
+
*
|
|
475
|
+
* @param {string} keyIdOrAlias - Key ID or alias name
|
|
476
|
+
* @returns {Promise<Object>} Key metadata
|
|
477
|
+
*/
|
|
478
|
+
async describeKmsKey(keyIdOrAlias) {
|
|
479
|
+
const kms = this.getKMSClient();
|
|
480
|
+
|
|
481
|
+
try {
|
|
482
|
+
const response = await kms.send(new DescribeKeyCommand({
|
|
483
|
+
KeyId: keyIdOrAlias,
|
|
484
|
+
}));
|
|
485
|
+
|
|
486
|
+
return response.KeyMetadata;
|
|
487
|
+
} catch (error) {
|
|
488
|
+
throw new Error(`Failed to describe KMS key ${keyIdOrAlias}: ${error.message}`);
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
|
|
472
492
|
/**
|
|
473
493
|
* Describe CloudFormation stack
|
|
474
|
-
*
|
|
494
|
+
*
|
|
475
495
|
* @param {string} stackName - Name of the CloudFormation stack
|
|
476
496
|
* @returns {Promise<Object>} Stack details including outputs
|
|
477
497
|
*/
|
|
@@ -73,9 +73,10 @@ async function gatherDiscoveredResources(appDefinition) {
|
|
|
73
73
|
// Build discovery configuration
|
|
74
74
|
const stage = process.env.SLS_STAGE || 'dev';
|
|
75
75
|
const stackName = `${appDefinition.name || 'create-frigg-app'}-${stage}`;
|
|
76
|
+
const serviceName = appDefinition.name || 'create-frigg-app';
|
|
76
77
|
|
|
77
78
|
// Try CloudFormation-first discovery
|
|
78
|
-
const cfDiscovery = new CloudFormationDiscovery(provider);
|
|
79
|
+
const cfDiscovery = new CloudFormationDiscovery(provider, { serviceName, stage });
|
|
79
80
|
const stackResources = await cfDiscovery.discoverFromStack(stackName);
|
|
80
81
|
|
|
81
82
|
// Validate CF discovery results - only use if contains useful data
|
|
@@ -84,19 +85,26 @@ async function gatherDiscoveredResources(appDefinition) {
|
|
|
84
85
|
const hasAuroraData = stackResources?.auroraClusterId;
|
|
85
86
|
const hasSomeUsefulData = hasVpcData || hasKmsData || hasAuroraData;
|
|
86
87
|
|
|
88
|
+
// Check if we're in isolated mode (each stage gets its own VPC/Aurora)
|
|
89
|
+
const isIsolatedMode = appDefinition.managementMode === 'managed' &&
|
|
90
|
+
appDefinition.vpcIsolation === 'isolated';
|
|
91
|
+
|
|
87
92
|
if (stackResources && hasSomeUsefulData) {
|
|
88
93
|
console.log(' ✓ Discovered resources from existing CloudFormation stack');
|
|
89
94
|
console.log('✅ Cloud resource discovery completed successfully!');
|
|
90
95
|
return stackResources;
|
|
91
96
|
}
|
|
92
97
|
|
|
93
|
-
// In isolated mode,
|
|
94
|
-
//
|
|
95
|
-
|
|
96
|
-
|
|
98
|
+
// In isolated mode, NEVER fall back to AWS discovery for VPC/Aurora
|
|
99
|
+
// These resources must be isolated per stage, so we either:
|
|
100
|
+
// 1. Use resources from THIS stage's CloudFormation stack (handled above)
|
|
101
|
+
// 2. Return empty to CREATE fresh isolated resources for this stage
|
|
102
|
+
if (isIsolatedMode) {
|
|
103
|
+
console.log(' ℹ Isolated mode: No CloudFormation stack or no VPC/Aurora in stack');
|
|
104
|
+
console.log(' ℹ Will create fresh isolated VPC/Aurora for this stage');
|
|
105
|
+
console.log(' ℹ Checking for shared KMS key...');
|
|
97
106
|
|
|
98
|
-
//
|
|
99
|
-
// Pass serviceName and stage to search for stage-specific alias
|
|
107
|
+
// KMS keys CAN be shared across stages (encryption keys are safe to reuse)
|
|
100
108
|
const kmsDiscovery = new KmsDiscovery(provider);
|
|
101
109
|
const kmsConfig = {
|
|
102
110
|
serviceName: appDefinition.name || 'create-frigg-app',
|
|
@@ -107,12 +115,12 @@ async function gatherDiscoveredResources(appDefinition) {
|
|
|
107
115
|
|
|
108
116
|
if (kmsResult?.defaultKmsKeyId) {
|
|
109
117
|
console.log(' ✓ Found shared KMS key (can be reused across stages)');
|
|
110
|
-
console.log('✅ Cloud resource discovery completed
|
|
118
|
+
console.log('✅ Cloud resource discovery completed - will create isolated VPC/Aurora!');
|
|
111
119
|
return kmsResult;
|
|
112
120
|
}
|
|
113
121
|
|
|
114
122
|
console.log(' ℹ No existing KMS key found - will create new one');
|
|
115
|
-
console.log('✅ Cloud resource discovery completed
|
|
123
|
+
console.log('✅ Cloud resource discovery completed - will create fresh isolated resources!');
|
|
116
124
|
return {};
|
|
117
125
|
}
|
|
118
126
|
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@friggframework/devtools",
|
|
3
3
|
"prettier": "@friggframework/prettier-config",
|
|
4
|
-
"version": "2.0.0--canary.461.
|
|
4
|
+
"version": "2.0.0--canary.461.39e4094.0",
|
|
5
5
|
"dependencies": {
|
|
6
6
|
"@aws-sdk/client-ec2": "^3.835.0",
|
|
7
7
|
"@aws-sdk/client-kms": "^3.835.0",
|
|
@@ -11,8 +11,8 @@
|
|
|
11
11
|
"@babel/eslint-parser": "^7.18.9",
|
|
12
12
|
"@babel/parser": "^7.25.3",
|
|
13
13
|
"@babel/traverse": "^7.25.3",
|
|
14
|
-
"@friggframework/schemas": "2.0.0--canary.461.
|
|
15
|
-
"@friggframework/test": "2.0.0--canary.461.
|
|
14
|
+
"@friggframework/schemas": "2.0.0--canary.461.39e4094.0",
|
|
15
|
+
"@friggframework/test": "2.0.0--canary.461.39e4094.0",
|
|
16
16
|
"@hapi/boom": "^10.0.1",
|
|
17
17
|
"@inquirer/prompts": "^5.3.8",
|
|
18
18
|
"axios": "^1.7.2",
|
|
@@ -34,8 +34,8 @@
|
|
|
34
34
|
"serverless-http": "^2.7.0"
|
|
35
35
|
},
|
|
36
36
|
"devDependencies": {
|
|
37
|
-
"@friggframework/eslint-config": "2.0.0--canary.461.
|
|
38
|
-
"@friggframework/prettier-config": "2.0.0--canary.461.
|
|
37
|
+
"@friggframework/eslint-config": "2.0.0--canary.461.39e4094.0",
|
|
38
|
+
"@friggframework/prettier-config": "2.0.0--canary.461.39e4094.0",
|
|
39
39
|
"aws-sdk-client-mock": "^4.1.0",
|
|
40
40
|
"aws-sdk-client-mock-jest": "^4.1.0",
|
|
41
41
|
"jest": "^30.1.3",
|
|
@@ -70,5 +70,5 @@
|
|
|
70
70
|
"publishConfig": {
|
|
71
71
|
"access": "public"
|
|
72
72
|
},
|
|
73
|
-
"gitHead": "
|
|
73
|
+
"gitHead": "39e40947472c29e3847fa53dad03ab1354568e56"
|
|
74
74
|
}
|