@friggframework/devtools 2.0.0--canary.428.edce2a7.0 → 2.0.0--canary.428.4dfa677.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,10 +1,10 @@
|
|
|
1
|
-
let EC2Client, DescribeVpcsCommand, DescribeSubnetsCommand, DescribeSecurityGroupsCommand, DescribeRouteTablesCommand, DescribeNatGatewaysCommand, DescribeAddressesCommand;
|
|
1
|
+
let EC2Client, DescribeVpcsCommand, DescribeSubnetsCommand, DescribeSecurityGroupsCommand, DescribeRouteTablesCommand, DescribeNatGatewaysCommand, DescribeAddressesCommand, DescribeInternetGatewaysCommand;
|
|
2
2
|
let KMSClient, ListKeysCommand, DescribeKeyCommand;
|
|
3
3
|
let STSClient, GetCallerIdentityCommand;
|
|
4
4
|
|
|
5
5
|
function loadEC2() {
|
|
6
6
|
if (!EC2Client) {
|
|
7
|
-
({ EC2Client, DescribeVpcsCommand, DescribeSubnetsCommand, DescribeSecurityGroupsCommand, DescribeRouteTablesCommand, DescribeNatGatewaysCommand, DescribeAddressesCommand } = require('@aws-sdk/client-ec2'));
|
|
7
|
+
({ EC2Client, DescribeVpcsCommand, DescribeSubnetsCommand, DescribeSecurityGroupsCommand, DescribeRouteTablesCommand, DescribeNatGatewaysCommand, DescribeAddressesCommand, DescribeInternetGatewaysCommand } = require('@aws-sdk/client-ec2'));
|
|
8
8
|
}
|
|
9
9
|
}
|
|
10
10
|
|
|
@@ -289,9 +289,9 @@ class AWSDiscovery {
|
|
|
289
289
|
}
|
|
290
290
|
]
|
|
291
291
|
});
|
|
292
|
-
|
|
292
|
+
|
|
293
293
|
const response = await this.ec2Client.send(command);
|
|
294
|
-
|
|
294
|
+
|
|
295
295
|
if (!response.Subnets || response.Subnets.length === 0) {
|
|
296
296
|
throw new Error(`No subnets found in VPC ${vpcId}`);
|
|
297
297
|
}
|
|
@@ -308,10 +308,15 @@ class AWSDiscovery {
|
|
|
308
308
|
}
|
|
309
309
|
|
|
310
310
|
if (publicSubnets.length === 0) {
|
|
311
|
-
|
|
311
|
+
// If no public subnets found, we need to create one or inform the user
|
|
312
|
+
console.warn(`WARNING: No public subnets found in VPC ${vpcId}`);
|
|
313
|
+
console.warn('A public subnet with Internet Gateway route is required for NAT Gateway placement');
|
|
314
|
+
console.warn('Please create a public subnet or use VPC endpoints instead');
|
|
315
|
+
return null; // Return null instead of throwing to allow graceful handling
|
|
312
316
|
}
|
|
313
317
|
|
|
314
318
|
// Return first public subnet for NAT Gateway
|
|
319
|
+
console.log(`Found ${publicSubnets.length} public subnets, using ${publicSubnets[0].SubnetId} for NAT Gateway`);
|
|
315
320
|
return publicSubnets[0];
|
|
316
321
|
} catch (error) {
|
|
317
322
|
console.error('Error finding public subnets:', error);
|
|
@@ -554,7 +559,7 @@ class AWSDiscovery {
|
|
|
554
559
|
defaultSecurityGroupId: securityGroup.GroupId,
|
|
555
560
|
privateSubnetId1: privateSubnets[0]?.SubnetId,
|
|
556
561
|
privateSubnetId2: privateSubnets[1]?.SubnetId || privateSubnets[0]?.SubnetId,
|
|
557
|
-
publicSubnetId: publicSubnet
|
|
562
|
+
publicSubnetId: publicSubnet?.SubnetId || null, // May be null if no public subnet exists
|
|
558
563
|
privateRouteTableId: routeTable.RouteTableId,
|
|
559
564
|
defaultKmsKeyId: kmsKeyArn,
|
|
560
565
|
existingNatGatewayId: natGatewayId,
|
|
@@ -565,6 +570,40 @@ class AWSDiscovery {
|
|
|
565
570
|
throw error;
|
|
566
571
|
}
|
|
567
572
|
}
|
|
573
|
+
|
|
574
|
+
/**
|
|
575
|
+
* Find an existing Internet Gateway attached to the VPC
|
|
576
|
+
* @param {string} vpcId - VPC ID to search in
|
|
577
|
+
* @returns {Promise<Object|null>} Internet Gateway object or null if none found
|
|
578
|
+
*/
|
|
579
|
+
async findInternetGateway(vpcId) {
|
|
580
|
+
try {
|
|
581
|
+
const command = new DescribeInternetGatewaysCommand({
|
|
582
|
+
Filters: [
|
|
583
|
+
{
|
|
584
|
+
Name: 'attachment.vpc-id',
|
|
585
|
+
Values: [vpcId]
|
|
586
|
+
},
|
|
587
|
+
{
|
|
588
|
+
Name: 'attachment.state',
|
|
589
|
+
Values: ['available']
|
|
590
|
+
}
|
|
591
|
+
]
|
|
592
|
+
});
|
|
593
|
+
|
|
594
|
+
const response = await this.ec2Client.send(command);
|
|
595
|
+
|
|
596
|
+
if (response.InternetGateways && response.InternetGateways.length > 0) {
|
|
597
|
+
console.log(`Found existing Internet Gateway: ${response.InternetGateways[0].InternetGatewayId}`);
|
|
598
|
+
return response.InternetGateways[0];
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
return null;
|
|
602
|
+
} catch (error) {
|
|
603
|
+
console.warn('Error finding Internet Gateway:', error.message);
|
|
604
|
+
return null;
|
|
605
|
+
}
|
|
606
|
+
}
|
|
568
607
|
}
|
|
569
608
|
|
|
570
609
|
module.exports = { AWSDiscovery };
|
|
@@ -1103,6 +1103,89 @@ const composeServerlessDefinition = async (AppDefinition) => {
|
|
|
1103
1103
|
};
|
|
1104
1104
|
}
|
|
1105
1105
|
|
|
1106
|
+
// If no public subnet exists, create one for NAT Gateway placement
|
|
1107
|
+
if (!discoveredResources.publicSubnetId) {
|
|
1108
|
+
console.log('No public subnet found, creating one for NAT Gateway placement...');
|
|
1109
|
+
|
|
1110
|
+
// Check if Internet Gateway exists or create one
|
|
1111
|
+
if (!discoveredResources.internetGatewayId) {
|
|
1112
|
+
definition.resources.Resources.FriggInternetGateway = {
|
|
1113
|
+
Type: 'AWS::EC2::InternetGateway',
|
|
1114
|
+
Properties: {
|
|
1115
|
+
Tags: [
|
|
1116
|
+
{
|
|
1117
|
+
Key: 'Name',
|
|
1118
|
+
Value: '${self:service}-${self:provider.stage}-igw',
|
|
1119
|
+
},
|
|
1120
|
+
],
|
|
1121
|
+
},
|
|
1122
|
+
};
|
|
1123
|
+
|
|
1124
|
+
definition.resources.Resources.FriggIGWAttachment = {
|
|
1125
|
+
Type: 'AWS::EC2::VPCGatewayAttachment',
|
|
1126
|
+
Properties: {
|
|
1127
|
+
VpcId: discoveredResources.defaultVpcId,
|
|
1128
|
+
InternetGatewayId: { Ref: 'FriggInternetGateway' },
|
|
1129
|
+
},
|
|
1130
|
+
};
|
|
1131
|
+
}
|
|
1132
|
+
|
|
1133
|
+
// Create a small public subnet for NAT Gateway
|
|
1134
|
+
definition.resources.Resources.FriggPublicSubnet = {
|
|
1135
|
+
Type: 'AWS::EC2::Subnet',
|
|
1136
|
+
Properties: {
|
|
1137
|
+
VpcId: discoveredResources.defaultVpcId,
|
|
1138
|
+
CidrBlock: '${self:custom.publicSubnetCidr, "172.31.250.0/24"}', // Small /24 subnet
|
|
1139
|
+
AvailabilityZone: '${self:provider.region}a',
|
|
1140
|
+
MapPublicIpOnLaunch: true,
|
|
1141
|
+
Tags: [
|
|
1142
|
+
{
|
|
1143
|
+
Key: 'Name',
|
|
1144
|
+
Value: '${self:service}-${self:provider.stage}-public-subnet',
|
|
1145
|
+
},
|
|
1146
|
+
{
|
|
1147
|
+
Key: 'Type',
|
|
1148
|
+
Value: 'Public',
|
|
1149
|
+
},
|
|
1150
|
+
],
|
|
1151
|
+
},
|
|
1152
|
+
};
|
|
1153
|
+
|
|
1154
|
+
// Create route table for public subnet
|
|
1155
|
+
definition.resources.Resources.FriggPublicRouteTable = {
|
|
1156
|
+
Type: 'AWS::EC2::RouteTable',
|
|
1157
|
+
Properties: {
|
|
1158
|
+
VpcId: discoveredResources.defaultVpcId,
|
|
1159
|
+
Tags: [
|
|
1160
|
+
{
|
|
1161
|
+
Key: 'Name',
|
|
1162
|
+
Value: '${self:service}-${self:provider.stage}-public-rt',
|
|
1163
|
+
},
|
|
1164
|
+
],
|
|
1165
|
+
},
|
|
1166
|
+
};
|
|
1167
|
+
|
|
1168
|
+
// Add route to Internet Gateway
|
|
1169
|
+
definition.resources.Resources.FriggPublicRoute = {
|
|
1170
|
+
Type: 'AWS::EC2::Route',
|
|
1171
|
+
DependsOn: discoveredResources.internetGatewayId ? [] : 'FriggIGWAttachment',
|
|
1172
|
+
Properties: {
|
|
1173
|
+
RouteTableId: { Ref: 'FriggPublicRouteTable' },
|
|
1174
|
+
DestinationCidrBlock: '0.0.0.0/0',
|
|
1175
|
+
GatewayId: discoveredResources.internetGatewayId || { Ref: 'FriggInternetGateway' },
|
|
1176
|
+
},
|
|
1177
|
+
};
|
|
1178
|
+
|
|
1179
|
+
// Associate public subnet with public route table
|
|
1180
|
+
definition.resources.Resources.FriggPublicSubnetRouteTableAssociation = {
|
|
1181
|
+
Type: 'AWS::EC2::SubnetRouteTableAssociation',
|
|
1182
|
+
Properties: {
|
|
1183
|
+
SubnetId: { Ref: 'FriggPublicSubnet' },
|
|
1184
|
+
RouteTableId: { Ref: 'FriggPublicRouteTable' },
|
|
1185
|
+
},
|
|
1186
|
+
};
|
|
1187
|
+
}
|
|
1188
|
+
|
|
1106
1189
|
definition.resources.Resources.FriggNATGateway = {
|
|
1107
1190
|
Type: 'AWS::EC2::NatGateway',
|
|
1108
1191
|
Properties: {
|
|
@@ -1113,9 +1196,7 @@ const composeServerlessDefinition = async (AppDefinition) => {
|
|
|
1113
1196
|
'AllocationId',
|
|
1114
1197
|
],
|
|
1115
1198
|
},
|
|
1116
|
-
SubnetId:
|
|
1117
|
-
discoveredResources.publicSubnetId ||
|
|
1118
|
-
discoveredResources.privateSubnetId1, // Use first discovered subnet if no public subnet found
|
|
1199
|
+
SubnetId: discoveredResources.publicSubnetId || { Ref: 'FriggPublicSubnet' },
|
|
1119
1200
|
Tags: [
|
|
1120
1201
|
{
|
|
1121
1202
|
Key: 'Name',
|
|
@@ -1155,12 +1236,15 @@ const composeServerlessDefinition = async (AppDefinition) => {
|
|
|
1155
1236
|
};
|
|
1156
1237
|
|
|
1157
1238
|
// Associate Lambda subnets with NAT Gateway route table
|
|
1239
|
+
// Note: This will only work if the subnets aren't already associated with another route table
|
|
1240
|
+
// If deployment fails, manually associate the subnets with the correct route table in AWS Console
|
|
1158
1241
|
definition.resources.Resources.FriggSubnet1RouteAssociation = {
|
|
1159
1242
|
Type: 'AWS::EC2::SubnetRouteTableAssociation',
|
|
1160
1243
|
Properties: {
|
|
1161
1244
|
SubnetId: vpcConfig.subnetIds[0],
|
|
1162
1245
|
RouteTableId: { Ref: 'FriggLambdaRouteTable' },
|
|
1163
1246
|
},
|
|
1247
|
+
DependsOn: 'FriggLambdaRouteTable',
|
|
1164
1248
|
};
|
|
1165
1249
|
|
|
1166
1250
|
definition.resources.Resources.FriggSubnet2RouteAssociation = {
|
|
@@ -1169,6 +1253,7 @@ const composeServerlessDefinition = async (AppDefinition) => {
|
|
|
1169
1253
|
SubnetId: vpcConfig.subnetIds[1],
|
|
1170
1254
|
RouteTableId: { Ref: 'FriggLambdaRouteTable' },
|
|
1171
1255
|
},
|
|
1256
|
+
DependsOn: 'FriggLambdaRouteTable',
|
|
1172
1257
|
};
|
|
1173
1258
|
|
|
1174
1259
|
// Add VPC endpoints for AWS service optimization (optional but recommended)
|
|
@@ -1194,6 +1279,65 @@ const composeServerlessDefinition = async (AppDefinition) => {
|
|
|
1194
1279
|
RouteTableIds: [{ Ref: 'FriggLambdaRouteTable' }],
|
|
1195
1280
|
},
|
|
1196
1281
|
};
|
|
1282
|
+
|
|
1283
|
+
// Add KMS VPC endpoint if using KMS encryption
|
|
1284
|
+
if (AppDefinition.encryption?.fieldLevelEncryptionMethod === 'kms') {
|
|
1285
|
+
// Create security group for VPC endpoints if it doesn't exist
|
|
1286
|
+
if (!definition.resources.Resources.VPCEndpointSecurityGroup) {
|
|
1287
|
+
definition.resources.Resources.VPCEndpointSecurityGroup = {
|
|
1288
|
+
Type: 'AWS::EC2::SecurityGroup',
|
|
1289
|
+
Properties: {
|
|
1290
|
+
GroupDescription: 'Security group for VPC endpoints',
|
|
1291
|
+
VpcId: discoveredResources.defaultVpcId,
|
|
1292
|
+
SecurityGroupIngress: [
|
|
1293
|
+
{
|
|
1294
|
+
IpProtocol: 'tcp',
|
|
1295
|
+
FromPort: 443,
|
|
1296
|
+
ToPort: 443,
|
|
1297
|
+
CidrIp: '172.31.0.0/16', // VPC CIDR
|
|
1298
|
+
},
|
|
1299
|
+
],
|
|
1300
|
+
Tags: [
|
|
1301
|
+
{
|
|
1302
|
+
Key: 'Name',
|
|
1303
|
+
Value: '${self:service}-${self:provider.stage}-vpc-endpoints-sg',
|
|
1304
|
+
},
|
|
1305
|
+
],
|
|
1306
|
+
},
|
|
1307
|
+
};
|
|
1308
|
+
}
|
|
1309
|
+
|
|
1310
|
+
definition.resources.Resources.VPCEndpointKMS = {
|
|
1311
|
+
Type: 'AWS::EC2::VPCEndpoint',
|
|
1312
|
+
Properties: {
|
|
1313
|
+
VpcId: discoveredResources.defaultVpcId,
|
|
1314
|
+
ServiceName: 'com.amazonaws.${self:provider.region}.kms',
|
|
1315
|
+
VpcEndpointType: 'Interface',
|
|
1316
|
+
SubnetIds: vpcConfig.subnetIds,
|
|
1317
|
+
SecurityGroupIds: [
|
|
1318
|
+
{ Ref: 'VPCEndpointSecurityGroup' },
|
|
1319
|
+
],
|
|
1320
|
+
PrivateDnsEnabled: true,
|
|
1321
|
+
},
|
|
1322
|
+
};
|
|
1323
|
+
|
|
1324
|
+
// Also add Secrets Manager endpoint if using Secrets Manager
|
|
1325
|
+
if (AppDefinition.secretsManager?.enable === true) {
|
|
1326
|
+
definition.resources.Resources.VPCEndpointSecretsManager = {
|
|
1327
|
+
Type: 'AWS::EC2::VPCEndpoint',
|
|
1328
|
+
Properties: {
|
|
1329
|
+
VpcId: discoveredResources.defaultVpcId,
|
|
1330
|
+
ServiceName: 'com.amazonaws.${self:provider.region}.secretsmanager',
|
|
1331
|
+
VpcEndpointType: 'Interface',
|
|
1332
|
+
SubnetIds: vpcConfig.subnetIds,
|
|
1333
|
+
SecurityGroupIds: [
|
|
1334
|
+
{ Ref: 'VPCEndpointSecurityGroup' },
|
|
1335
|
+
],
|
|
1336
|
+
PrivateDnsEnabled: true,
|
|
1337
|
+
},
|
|
1338
|
+
};
|
|
1339
|
+
}
|
|
1340
|
+
}
|
|
1197
1341
|
}
|
|
1198
1342
|
}
|
|
1199
1343
|
}
|
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.428.
|
|
4
|
+
"version": "2.0.0--canary.428.4dfa677.0",
|
|
5
5
|
"dependencies": {
|
|
6
6
|
"@aws-sdk/client-ec2": "^3.835.0",
|
|
7
7
|
"@aws-sdk/client-kms": "^3.835.0",
|
|
@@ -9,8 +9,8 @@
|
|
|
9
9
|
"@babel/eslint-parser": "^7.18.9",
|
|
10
10
|
"@babel/parser": "^7.25.3",
|
|
11
11
|
"@babel/traverse": "^7.25.3",
|
|
12
|
-
"@friggframework/schemas": "2.0.0--canary.428.
|
|
13
|
-
"@friggframework/test": "2.0.0--canary.428.
|
|
12
|
+
"@friggframework/schemas": "2.0.0--canary.428.4dfa677.0",
|
|
13
|
+
"@friggframework/test": "2.0.0--canary.428.4dfa677.0",
|
|
14
14
|
"@hapi/boom": "^10.0.1",
|
|
15
15
|
"@inquirer/prompts": "^5.3.8",
|
|
16
16
|
"axios": "^1.7.2",
|
|
@@ -32,8 +32,8 @@
|
|
|
32
32
|
"serverless-http": "^2.7.0"
|
|
33
33
|
},
|
|
34
34
|
"devDependencies": {
|
|
35
|
-
"@friggframework/eslint-config": "2.0.0--canary.428.
|
|
36
|
-
"@friggframework/prettier-config": "2.0.0--canary.428.
|
|
35
|
+
"@friggframework/eslint-config": "2.0.0--canary.428.4dfa677.0",
|
|
36
|
+
"@friggframework/prettier-config": "2.0.0--canary.428.4dfa677.0",
|
|
37
37
|
"jest": "^30.1.3",
|
|
38
38
|
"prettier": "^2.7.1",
|
|
39
39
|
"serverless": "3.39.0",
|
|
@@ -66,5 +66,5 @@
|
|
|
66
66
|
"publishConfig": {
|
|
67
67
|
"access": "public"
|
|
68
68
|
},
|
|
69
|
-
"gitHead": "
|
|
69
|
+
"gitHead": "4dfa6772d058ce8bb7a6ecc6313c070d57e4bc23"
|
|
70
70
|
}
|