@friggframework/devtools 2.0.0-next.33 → 2.0.0-next.35
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/deploy-command/index.js +4 -3
- package/infrastructure/AWS-IAM-CREDENTIAL-NEEDS.md +442 -411
- package/infrastructure/GENERATE-IAM-DOCS.md +91 -66
- package/infrastructure/frigg-deployment-iam-stack.yaml +22 -0
- package/infrastructure/iam-generator.js +210 -229
- package/infrastructure/serverless-template.js +426 -243
- package/package.json +6 -6
|
@@ -8,9 +8,12 @@ const { AWSDiscovery } = require('./aws-discovery');
|
|
|
8
8
|
* @returns {boolean} True if discovery should run
|
|
9
9
|
*/
|
|
10
10
|
const shouldRunDiscovery = (AppDefinition) => {
|
|
11
|
-
return
|
|
12
|
-
AppDefinition.
|
|
13
|
-
AppDefinition.
|
|
11
|
+
return (
|
|
12
|
+
AppDefinition.vpc?.enable === true ||
|
|
13
|
+
AppDefinition.encryption?.useDefaultKMSForFieldLevelEncryption ===
|
|
14
|
+
true ||
|
|
15
|
+
AppDefinition.ssm?.enable === true
|
|
16
|
+
);
|
|
14
17
|
};
|
|
15
18
|
|
|
16
19
|
/**
|
|
@@ -32,7 +35,9 @@ const findNodeModulesPath = () => {
|
|
|
32
35
|
const potentialPath = path.join(currentDir, 'node_modules');
|
|
33
36
|
if (fs.existsSync(potentialPath)) {
|
|
34
37
|
nodeModulesPath = potentialPath;
|
|
35
|
-
console.log(
|
|
38
|
+
console.log(
|
|
39
|
+
`Found node_modules at: ${nodeModulesPath} (method 1)`
|
|
40
|
+
);
|
|
36
41
|
break;
|
|
37
42
|
}
|
|
38
43
|
// Move up one directory
|
|
@@ -49,10 +54,14 @@ const findNodeModulesPath = () => {
|
|
|
49
54
|
try {
|
|
50
55
|
// This requires child_process, so let's require it here
|
|
51
56
|
const { execSync } = require('node:child_process');
|
|
52
|
-
const npmRoot = execSync('npm root', {
|
|
57
|
+
const npmRoot = execSync('npm root', {
|
|
58
|
+
encoding: 'utf8',
|
|
59
|
+
}).trim();
|
|
53
60
|
if (fs.existsSync(npmRoot)) {
|
|
54
61
|
nodeModulesPath = npmRoot;
|
|
55
|
-
console.log(
|
|
62
|
+
console.log(
|
|
63
|
+
`Found node_modules at: ${nodeModulesPath} (method 2)`
|
|
64
|
+
);
|
|
56
65
|
}
|
|
57
66
|
} catch (npmError) {
|
|
58
67
|
console.error('Error executing npm root:', npmError);
|
|
@@ -65,10 +74,15 @@ const findNodeModulesPath = () => {
|
|
|
65
74
|
for (let i = 0; i < 5; i++) {
|
|
66
75
|
const packageJsonPath = path.join(currentDir, 'package.json');
|
|
67
76
|
if (fs.existsSync(packageJsonPath)) {
|
|
68
|
-
const potentialNodeModules = path.join(
|
|
77
|
+
const potentialNodeModules = path.join(
|
|
78
|
+
currentDir,
|
|
79
|
+
'node_modules'
|
|
80
|
+
);
|
|
69
81
|
if (fs.existsSync(potentialNodeModules)) {
|
|
70
82
|
nodeModulesPath = potentialNodeModules;
|
|
71
|
-
console.log(
|
|
83
|
+
console.log(
|
|
84
|
+
`Found node_modules at: ${nodeModulesPath} (method 3)`
|
|
85
|
+
);
|
|
72
86
|
break;
|
|
73
87
|
}
|
|
74
88
|
}
|
|
@@ -86,7 +100,9 @@ const findNodeModulesPath = () => {
|
|
|
86
100
|
return nodeModulesPath;
|
|
87
101
|
}
|
|
88
102
|
|
|
89
|
-
console.warn(
|
|
103
|
+
console.warn(
|
|
104
|
+
'Could not find node_modules path, falling back to default'
|
|
105
|
+
);
|
|
90
106
|
return path.resolve(process.cwd(), '../node_modules');
|
|
91
107
|
} catch (error) {
|
|
92
108
|
console.error('Error finding node_modules path:', error);
|
|
@@ -119,8 +135,13 @@ const modifyHandlerPaths = (functions) => {
|
|
|
119
135
|
if (functionDef?.handler?.includes('node_modules/')) {
|
|
120
136
|
// Replace node_modules/ with the actual path to node_modules/
|
|
121
137
|
const relativePath = path.relative(process.cwd(), nodeModulesPath);
|
|
122
|
-
functionDef.handler = functionDef.handler.replace(
|
|
123
|
-
|
|
138
|
+
functionDef.handler = functionDef.handler.replace(
|
|
139
|
+
'node_modules/',
|
|
140
|
+
`${relativePath}/`
|
|
141
|
+
);
|
|
142
|
+
console.log(
|
|
143
|
+
`Updated handler for ${functionName}: ${functionDef.handler}`
|
|
144
|
+
);
|
|
124
145
|
}
|
|
125
146
|
}
|
|
126
147
|
|
|
@@ -145,9 +166,12 @@ const createVPCInfrastructure = (AppDefinition) => {
|
|
|
145
166
|
EnableDnsHostnames: true,
|
|
146
167
|
EnableDnsSupport: true,
|
|
147
168
|
Tags: [
|
|
148
|
-
{
|
|
149
|
-
|
|
150
|
-
|
|
169
|
+
{
|
|
170
|
+
Key: 'Name',
|
|
171
|
+
Value: '${self:service}-${self:provider.stage}-vpc',
|
|
172
|
+
},
|
|
173
|
+
],
|
|
174
|
+
},
|
|
151
175
|
},
|
|
152
176
|
|
|
153
177
|
// Internet Gateway
|
|
@@ -155,9 +179,12 @@ const createVPCInfrastructure = (AppDefinition) => {
|
|
|
155
179
|
Type: 'AWS::EC2::InternetGateway',
|
|
156
180
|
Properties: {
|
|
157
181
|
Tags: [
|
|
158
|
-
{
|
|
159
|
-
|
|
160
|
-
|
|
182
|
+
{
|
|
183
|
+
Key: 'Name',
|
|
184
|
+
Value: '${self:service}-${self:provider.stage}-igw',
|
|
185
|
+
},
|
|
186
|
+
],
|
|
187
|
+
},
|
|
161
188
|
},
|
|
162
189
|
|
|
163
190
|
// Attach Internet Gateway to VPC
|
|
@@ -165,8 +192,8 @@ const createVPCInfrastructure = (AppDefinition) => {
|
|
|
165
192
|
Type: 'AWS::EC2::VPCGatewayAttachment',
|
|
166
193
|
Properties: {
|
|
167
194
|
VpcId: { Ref: 'FriggVPC' },
|
|
168
|
-
InternetGatewayId: { Ref: 'FriggInternetGateway' }
|
|
169
|
-
}
|
|
195
|
+
InternetGatewayId: { Ref: 'FriggInternetGateway' },
|
|
196
|
+
},
|
|
170
197
|
},
|
|
171
198
|
|
|
172
199
|
// Public Subnet for NAT Gateway
|
|
@@ -178,9 +205,12 @@ const createVPCInfrastructure = (AppDefinition) => {
|
|
|
178
205
|
AvailabilityZone: { 'Fn::Select': [0, { 'Fn::GetAZs': '' }] },
|
|
179
206
|
MapPublicIpOnLaunch: true,
|
|
180
207
|
Tags: [
|
|
181
|
-
{
|
|
182
|
-
|
|
183
|
-
|
|
208
|
+
{
|
|
209
|
+
Key: 'Name',
|
|
210
|
+
Value: '${self:service}-${self:provider.stage}-public-subnet',
|
|
211
|
+
},
|
|
212
|
+
],
|
|
213
|
+
},
|
|
184
214
|
},
|
|
185
215
|
|
|
186
216
|
// Private Subnet 1 for Lambda
|
|
@@ -191,9 +221,12 @@ const createVPCInfrastructure = (AppDefinition) => {
|
|
|
191
221
|
CidrBlock: '10.0.2.0/24',
|
|
192
222
|
AvailabilityZone: { 'Fn::Select': [0, { 'Fn::GetAZs': '' }] },
|
|
193
223
|
Tags: [
|
|
194
|
-
{
|
|
195
|
-
|
|
196
|
-
|
|
224
|
+
{
|
|
225
|
+
Key: 'Name',
|
|
226
|
+
Value: '${self:service}-${self:provider.stage}-private-subnet-1',
|
|
227
|
+
},
|
|
228
|
+
],
|
|
229
|
+
},
|
|
197
230
|
},
|
|
198
231
|
|
|
199
232
|
// Private Subnet 2 for Lambda (different AZ for redundancy)
|
|
@@ -204,9 +237,12 @@ const createVPCInfrastructure = (AppDefinition) => {
|
|
|
204
237
|
CidrBlock: '10.0.3.0/24',
|
|
205
238
|
AvailabilityZone: { 'Fn::Select': [1, { 'Fn::GetAZs': '' }] },
|
|
206
239
|
Tags: [
|
|
207
|
-
{
|
|
208
|
-
|
|
209
|
-
|
|
240
|
+
{
|
|
241
|
+
Key: 'Name',
|
|
242
|
+
Value: '${self:service}-${self:provider.stage}-private-subnet-2',
|
|
243
|
+
},
|
|
244
|
+
],
|
|
245
|
+
},
|
|
210
246
|
},
|
|
211
247
|
|
|
212
248
|
// Elastic IP for NAT Gateway
|
|
@@ -215,22 +251,30 @@ const createVPCInfrastructure = (AppDefinition) => {
|
|
|
215
251
|
Properties: {
|
|
216
252
|
Domain: 'vpc',
|
|
217
253
|
Tags: [
|
|
218
|
-
{
|
|
219
|
-
|
|
254
|
+
{
|
|
255
|
+
Key: 'Name',
|
|
256
|
+
Value: '${self:service}-${self:provider.stage}-nat-eip',
|
|
257
|
+
},
|
|
258
|
+
],
|
|
220
259
|
},
|
|
221
|
-
DependsOn: 'FriggVPCGatewayAttachment'
|
|
260
|
+
DependsOn: 'FriggVPCGatewayAttachment',
|
|
222
261
|
},
|
|
223
262
|
|
|
224
263
|
// NAT Gateway for private subnet internet access
|
|
225
264
|
FriggNATGateway: {
|
|
226
265
|
Type: 'AWS::EC2::NatGateway',
|
|
227
266
|
Properties: {
|
|
228
|
-
AllocationId: {
|
|
267
|
+
AllocationId: {
|
|
268
|
+
'Fn::GetAtt': ['FriggNATGatewayEIP', 'AllocationId'],
|
|
269
|
+
},
|
|
229
270
|
SubnetId: { Ref: 'FriggPublicSubnet' },
|
|
230
271
|
Tags: [
|
|
231
|
-
{
|
|
232
|
-
|
|
233
|
-
|
|
272
|
+
{
|
|
273
|
+
Key: 'Name',
|
|
274
|
+
Value: '${self:service}-${self:provider.stage}-nat-gateway',
|
|
275
|
+
},
|
|
276
|
+
],
|
|
277
|
+
},
|
|
234
278
|
},
|
|
235
279
|
|
|
236
280
|
// Public Route Table
|
|
@@ -239,9 +283,12 @@ const createVPCInfrastructure = (AppDefinition) => {
|
|
|
239
283
|
Properties: {
|
|
240
284
|
VpcId: { Ref: 'FriggVPC' },
|
|
241
285
|
Tags: [
|
|
242
|
-
{
|
|
243
|
-
|
|
244
|
-
|
|
286
|
+
{
|
|
287
|
+
Key: 'Name',
|
|
288
|
+
Value: '${self:service}-${self:provider.stage}-public-rt',
|
|
289
|
+
},
|
|
290
|
+
],
|
|
291
|
+
},
|
|
245
292
|
},
|
|
246
293
|
|
|
247
294
|
// Public Route to Internet Gateway
|
|
@@ -250,9 +297,9 @@ const createVPCInfrastructure = (AppDefinition) => {
|
|
|
250
297
|
Properties: {
|
|
251
298
|
RouteTableId: { Ref: 'FriggPublicRouteTable' },
|
|
252
299
|
DestinationCidrBlock: '0.0.0.0/0',
|
|
253
|
-
GatewayId: { Ref: 'FriggInternetGateway' }
|
|
300
|
+
GatewayId: { Ref: 'FriggInternetGateway' },
|
|
254
301
|
},
|
|
255
|
-
DependsOn: 'FriggVPCGatewayAttachment'
|
|
302
|
+
DependsOn: 'FriggVPCGatewayAttachment',
|
|
256
303
|
},
|
|
257
304
|
|
|
258
305
|
// Associate Public Subnet with Public Route Table
|
|
@@ -260,8 +307,8 @@ const createVPCInfrastructure = (AppDefinition) => {
|
|
|
260
307
|
Type: 'AWS::EC2::SubnetRouteTableAssociation',
|
|
261
308
|
Properties: {
|
|
262
309
|
SubnetId: { Ref: 'FriggPublicSubnet' },
|
|
263
|
-
RouteTableId: { Ref: 'FriggPublicRouteTable' }
|
|
264
|
-
}
|
|
310
|
+
RouteTableId: { Ref: 'FriggPublicRouteTable' },
|
|
311
|
+
},
|
|
265
312
|
},
|
|
266
313
|
|
|
267
314
|
// Private Route Table
|
|
@@ -270,9 +317,12 @@ const createVPCInfrastructure = (AppDefinition) => {
|
|
|
270
317
|
Properties: {
|
|
271
318
|
VpcId: { Ref: 'FriggVPC' },
|
|
272
319
|
Tags: [
|
|
273
|
-
{
|
|
274
|
-
|
|
275
|
-
|
|
320
|
+
{
|
|
321
|
+
Key: 'Name',
|
|
322
|
+
Value: '${self:service}-${self:provider.stage}-private-rt',
|
|
323
|
+
},
|
|
324
|
+
],
|
|
325
|
+
},
|
|
276
326
|
},
|
|
277
327
|
|
|
278
328
|
// Private Route to NAT Gateway
|
|
@@ -281,8 +331,8 @@ const createVPCInfrastructure = (AppDefinition) => {
|
|
|
281
331
|
Properties: {
|
|
282
332
|
RouteTableId: { Ref: 'FriggPrivateRouteTable' },
|
|
283
333
|
DestinationCidrBlock: '0.0.0.0/0',
|
|
284
|
-
NatGatewayId: { Ref: 'FriggNATGateway' }
|
|
285
|
-
}
|
|
334
|
+
NatGatewayId: { Ref: 'FriggNATGateway' },
|
|
335
|
+
},
|
|
286
336
|
},
|
|
287
337
|
|
|
288
338
|
// Associate Private Subnet 1 with Private Route Table
|
|
@@ -290,8 +340,8 @@ const createVPCInfrastructure = (AppDefinition) => {
|
|
|
290
340
|
Type: 'AWS::EC2::SubnetRouteTableAssociation',
|
|
291
341
|
Properties: {
|
|
292
342
|
SubnetId: { Ref: 'FriggPrivateSubnet1' },
|
|
293
|
-
RouteTableId: { Ref: 'FriggPrivateRouteTable' }
|
|
294
|
-
}
|
|
343
|
+
RouteTableId: { Ref: 'FriggPrivateRouteTable' },
|
|
344
|
+
},
|
|
295
345
|
},
|
|
296
346
|
|
|
297
347
|
// Associate Private Subnet 2 with Private Route Table
|
|
@@ -299,8 +349,8 @@ const createVPCInfrastructure = (AppDefinition) => {
|
|
|
299
349
|
Type: 'AWS::EC2::SubnetRouteTableAssociation',
|
|
300
350
|
Properties: {
|
|
301
351
|
SubnetId: { Ref: 'FriggPrivateSubnet2' },
|
|
302
|
-
RouteTableId: { Ref: 'FriggPrivateRouteTable' }
|
|
303
|
-
}
|
|
352
|
+
RouteTableId: { Ref: 'FriggPrivateRouteTable' },
|
|
353
|
+
},
|
|
304
354
|
},
|
|
305
355
|
|
|
306
356
|
// Security Group for Lambda functions
|
|
@@ -315,35 +365,38 @@ const createVPCInfrastructure = (AppDefinition) => {
|
|
|
315
365
|
FromPort: 443,
|
|
316
366
|
ToPort: 443,
|
|
317
367
|
CidrIp: '0.0.0.0/0',
|
|
318
|
-
Description: 'HTTPS outbound'
|
|
368
|
+
Description: 'HTTPS outbound',
|
|
319
369
|
},
|
|
320
370
|
{
|
|
321
371
|
IpProtocol: 'tcp',
|
|
322
372
|
FromPort: 80,
|
|
323
373
|
ToPort: 80,
|
|
324
374
|
CidrIp: '0.0.0.0/0',
|
|
325
|
-
Description: 'HTTP outbound'
|
|
375
|
+
Description: 'HTTP outbound',
|
|
326
376
|
},
|
|
327
377
|
{
|
|
328
378
|
IpProtocol: 'tcp',
|
|
329
379
|
FromPort: 53,
|
|
330
380
|
ToPort: 53,
|
|
331
381
|
CidrIp: '0.0.0.0/0',
|
|
332
|
-
Description: 'DNS TCP'
|
|
382
|
+
Description: 'DNS TCP',
|
|
333
383
|
},
|
|
334
384
|
{
|
|
335
385
|
IpProtocol: 'udp',
|
|
336
386
|
FromPort: 53,
|
|
337
387
|
ToPort: 53,
|
|
338
388
|
CidrIp: '0.0.0.0/0',
|
|
339
|
-
Description: 'DNS UDP'
|
|
340
|
-
}
|
|
389
|
+
Description: 'DNS UDP',
|
|
390
|
+
},
|
|
341
391
|
],
|
|
342
392
|
Tags: [
|
|
343
|
-
{
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
393
|
+
{
|
|
394
|
+
Key: 'Name',
|
|
395
|
+
Value: '${self:service}-${self:provider.stage}-lambda-sg',
|
|
396
|
+
},
|
|
397
|
+
],
|
|
398
|
+
},
|
|
399
|
+
},
|
|
347
400
|
};
|
|
348
401
|
|
|
349
402
|
// Add VPC Endpoints for cost optimization
|
|
@@ -355,10 +408,8 @@ const createVPCInfrastructure = (AppDefinition) => {
|
|
|
355
408
|
VpcId: { Ref: 'FriggVPC' },
|
|
356
409
|
ServiceName: 'com.amazonaws.${self:provider.region}.s3',
|
|
357
410
|
VpcEndpointType: 'Gateway',
|
|
358
|
-
RouteTableIds: [
|
|
359
|
-
|
|
360
|
-
]
|
|
361
|
-
}
|
|
411
|
+
RouteTableIds: [{ Ref: 'FriggPrivateRouteTable' }],
|
|
412
|
+
},
|
|
362
413
|
};
|
|
363
414
|
|
|
364
415
|
// DynamoDB Gateway Endpoint (free)
|
|
@@ -368,14 +419,15 @@ const createVPCInfrastructure = (AppDefinition) => {
|
|
|
368
419
|
VpcId: { Ref: 'FriggVPC' },
|
|
369
420
|
ServiceName: 'com.amazonaws.${self:provider.region}.dynamodb',
|
|
370
421
|
VpcEndpointType: 'Gateway',
|
|
371
|
-
RouteTableIds: [
|
|
372
|
-
|
|
373
|
-
]
|
|
374
|
-
}
|
|
422
|
+
RouteTableIds: [{ Ref: 'FriggPrivateRouteTable' }],
|
|
423
|
+
},
|
|
375
424
|
};
|
|
376
425
|
|
|
377
426
|
// KMS Interface Endpoint (paid, but useful if using KMS)
|
|
378
|
-
if (
|
|
427
|
+
if (
|
|
428
|
+
AppDefinition.encryption?.useDefaultKMSForFieldLevelEncryption ===
|
|
429
|
+
true
|
|
430
|
+
) {
|
|
379
431
|
vpcResources.FriggKMSVPCEndpoint = {
|
|
380
432
|
Type: 'AWS::EC2::VPCEndpoint',
|
|
381
433
|
Properties: {
|
|
@@ -384,13 +436,13 @@ const createVPCInfrastructure = (AppDefinition) => {
|
|
|
384
436
|
VpcEndpointType: 'Interface',
|
|
385
437
|
SubnetIds: [
|
|
386
438
|
{ Ref: 'FriggPrivateSubnet1' },
|
|
387
|
-
{ Ref: 'FriggPrivateSubnet2' }
|
|
439
|
+
{ Ref: 'FriggPrivateSubnet2' },
|
|
388
440
|
],
|
|
389
441
|
SecurityGroupIds: [
|
|
390
|
-
{ Ref: 'FriggVPCEndpointSecurityGroup' }
|
|
442
|
+
{ Ref: 'FriggVPCEndpointSecurityGroup' },
|
|
391
443
|
],
|
|
392
|
-
PrivateDnsEnabled: true
|
|
393
|
-
}
|
|
444
|
+
PrivateDnsEnabled: true,
|
|
445
|
+
},
|
|
394
446
|
};
|
|
395
447
|
}
|
|
396
448
|
|
|
@@ -399,17 +451,16 @@ const createVPCInfrastructure = (AppDefinition) => {
|
|
|
399
451
|
Type: 'AWS::EC2::VPCEndpoint',
|
|
400
452
|
Properties: {
|
|
401
453
|
VpcId: { Ref: 'FriggVPC' },
|
|
402
|
-
ServiceName:
|
|
454
|
+
ServiceName:
|
|
455
|
+
'com.amazonaws.${self:provider.region}.secretsmanager',
|
|
403
456
|
VpcEndpointType: 'Interface',
|
|
404
457
|
SubnetIds: [
|
|
405
458
|
{ Ref: 'FriggPrivateSubnet1' },
|
|
406
|
-
{ Ref: 'FriggPrivateSubnet2' }
|
|
407
|
-
],
|
|
408
|
-
SecurityGroupIds: [
|
|
409
|
-
{ Ref: 'FriggVPCEndpointSecurityGroup' }
|
|
459
|
+
{ Ref: 'FriggPrivateSubnet2' },
|
|
410
460
|
],
|
|
411
|
-
|
|
412
|
-
|
|
461
|
+
SecurityGroupIds: [{ Ref: 'FriggVPCEndpointSecurityGroup' }],
|
|
462
|
+
PrivateDnsEnabled: true,
|
|
463
|
+
},
|
|
413
464
|
};
|
|
414
465
|
|
|
415
466
|
// Security Group for VPC Endpoints
|
|
@@ -423,14 +474,19 @@ const createVPCInfrastructure = (AppDefinition) => {
|
|
|
423
474
|
IpProtocol: 'tcp',
|
|
424
475
|
FromPort: 443,
|
|
425
476
|
ToPort: 443,
|
|
426
|
-
SourceSecurityGroupId: {
|
|
427
|
-
|
|
428
|
-
|
|
477
|
+
SourceSecurityGroupId: {
|
|
478
|
+
Ref: 'FriggLambdaSecurityGroup',
|
|
479
|
+
},
|
|
480
|
+
Description: 'HTTPS from Lambda',
|
|
481
|
+
},
|
|
429
482
|
],
|
|
430
483
|
Tags: [
|
|
431
|
-
{
|
|
432
|
-
|
|
433
|
-
|
|
484
|
+
{
|
|
485
|
+
Key: 'Name',
|
|
486
|
+
Value: '${self:service}-${self:provider.stage}-vpc-endpoint-sg',
|
|
487
|
+
},
|
|
488
|
+
],
|
|
489
|
+
},
|
|
434
490
|
};
|
|
435
491
|
}
|
|
436
492
|
|
|
@@ -456,7 +512,9 @@ const composeServerlessDefinition = async (AppDefinition) => {
|
|
|
456
512
|
|
|
457
513
|
// Run AWS discovery if needed
|
|
458
514
|
if (shouldRunDiscovery(AppDefinition)) {
|
|
459
|
-
console.log(
|
|
515
|
+
console.log(
|
|
516
|
+
'🔍 Running AWS resource discovery for serverless template...'
|
|
517
|
+
);
|
|
460
518
|
try {
|
|
461
519
|
const region = process.env.AWS_REGION || 'us-east-1';
|
|
462
520
|
const discovery = new AWSDiscovery(region);
|
|
@@ -464,7 +522,7 @@ const composeServerlessDefinition = async (AppDefinition) => {
|
|
|
464
522
|
const config = {
|
|
465
523
|
vpc: AppDefinition.vpc || {},
|
|
466
524
|
encryption: AppDefinition.encryption || {},
|
|
467
|
-
ssm: AppDefinition.ssm || {}
|
|
525
|
+
ssm: AppDefinition.ssm || {},
|
|
468
526
|
};
|
|
469
527
|
|
|
470
528
|
discoveredResources = await discovery.discoverResources(config);
|
|
@@ -473,14 +531,23 @@ const composeServerlessDefinition = async (AppDefinition) => {
|
|
|
473
531
|
if (discoveredResources.defaultVpcId) {
|
|
474
532
|
console.log(` VPC: ${discoveredResources.defaultVpcId}`);
|
|
475
533
|
}
|
|
476
|
-
if (
|
|
477
|
-
|
|
534
|
+
if (
|
|
535
|
+
discoveredResources.privateSubnetId1 &&
|
|
536
|
+
discoveredResources.privateSubnetId2
|
|
537
|
+
) {
|
|
538
|
+
console.log(
|
|
539
|
+
` Subnets: ${discoveredResources.privateSubnetId1}, ${discoveredResources.privateSubnetId2}`
|
|
540
|
+
);
|
|
478
541
|
}
|
|
479
542
|
if (discoveredResources.defaultSecurityGroupId) {
|
|
480
|
-
console.log(
|
|
543
|
+
console.log(
|
|
544
|
+
` Security Group: ${discoveredResources.defaultSecurityGroupId}`
|
|
545
|
+
);
|
|
481
546
|
}
|
|
482
547
|
if (discoveredResources.defaultKmsKeyId) {
|
|
483
|
-
console.log(
|
|
548
|
+
console.log(
|
|
549
|
+
` KMS Key: ${discoveredResources.defaultKmsKeyId}`
|
|
550
|
+
);
|
|
484
551
|
}
|
|
485
552
|
} catch (error) {
|
|
486
553
|
console.error('❌ AWS discovery failed:', error.message);
|
|
@@ -488,12 +555,28 @@ const composeServerlessDefinition = async (AppDefinition) => {
|
|
|
488
555
|
}
|
|
489
556
|
}
|
|
490
557
|
|
|
558
|
+
// Debug: log keys of env vars available during deploy (to verify GA -> Serverless pass-through)
|
|
559
|
+
try {
|
|
560
|
+
const envKeys = Object.keys(process.env || {}).sort();
|
|
561
|
+
console.log(
|
|
562
|
+
'Frigg deploy env keys (sample):',
|
|
563
|
+
envKeys.slice(0, 30),
|
|
564
|
+
`... total=${envKeys.length}`
|
|
565
|
+
);
|
|
566
|
+
} catch (e) {
|
|
567
|
+
console.log('Frigg deploy env keys: <unavailable>', e?.message);
|
|
568
|
+
}
|
|
569
|
+
|
|
491
570
|
const definition = {
|
|
492
571
|
frameworkVersion: '>=3.17.0',
|
|
493
572
|
service: AppDefinition.name || 'create-frigg-app',
|
|
494
573
|
package: {
|
|
495
574
|
individually: true,
|
|
496
|
-
exclude: [
|
|
575
|
+
exclude: [
|
|
576
|
+
'!**/node_modules/aws-sdk/**',
|
|
577
|
+
'!**/node_modules/@aws-sdk/**',
|
|
578
|
+
'!package.json',
|
|
579
|
+
],
|
|
497
580
|
},
|
|
498
581
|
useDotenv: true,
|
|
499
582
|
provider: {
|
|
@@ -505,14 +588,46 @@ const composeServerlessDefinition = async (AppDefinition) => {
|
|
|
505
588
|
environment: {
|
|
506
589
|
STAGE: '${opt:stage, "dev"}',
|
|
507
590
|
AWS_NODEJS_CONNECTION_REUSE_ENABLED: 1,
|
|
591
|
+
// Pass through FRIGG__ prefixed variables (stripping the prefix)
|
|
592
|
+
// This avoids CloudFormation validation errors from system variables
|
|
593
|
+
...Object.fromEntries(
|
|
594
|
+
Object.entries(process.env)
|
|
595
|
+
.filter(([key]) => key.startsWith('FRIGG__'))
|
|
596
|
+
.map(([key, value]) => [
|
|
597
|
+
key.replace('FRIGG__', ''),
|
|
598
|
+
value,
|
|
599
|
+
])
|
|
600
|
+
),
|
|
601
|
+
// Also include essential non-prefixed variables
|
|
602
|
+
...(process.env.NODE_ENV && { NODE_ENV: process.env.NODE_ENV }),
|
|
508
603
|
// Add discovered resources to environment if available
|
|
509
|
-
...(discoveredResources.defaultVpcId && {
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
...(discoveredResources.
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
604
|
+
...(discoveredResources.defaultVpcId && {
|
|
605
|
+
AWS_DISCOVERY_VPC_ID: discoveredResources.defaultVpcId,
|
|
606
|
+
}),
|
|
607
|
+
...(discoveredResources.defaultSecurityGroupId && {
|
|
608
|
+
AWS_DISCOVERY_SECURITY_GROUP_ID:
|
|
609
|
+
discoveredResources.defaultSecurityGroupId,
|
|
610
|
+
}),
|
|
611
|
+
...(discoveredResources.privateSubnetId1 && {
|
|
612
|
+
AWS_DISCOVERY_SUBNET_ID_1:
|
|
613
|
+
discoveredResources.privateSubnetId1,
|
|
614
|
+
}),
|
|
615
|
+
...(discoveredResources.privateSubnetId2 && {
|
|
616
|
+
AWS_DISCOVERY_SUBNET_ID_2:
|
|
617
|
+
discoveredResources.privateSubnetId2,
|
|
618
|
+
}),
|
|
619
|
+
...(discoveredResources.publicSubnetId && {
|
|
620
|
+
AWS_DISCOVERY_PUBLIC_SUBNET_ID:
|
|
621
|
+
discoveredResources.publicSubnetId,
|
|
622
|
+
}),
|
|
623
|
+
...(discoveredResources.defaultRouteTableId && {
|
|
624
|
+
AWS_DISCOVERY_ROUTE_TABLE_ID:
|
|
625
|
+
discoveredResources.defaultRouteTableId,
|
|
626
|
+
}),
|
|
627
|
+
...(discoveredResources.defaultKmsKeyId && {
|
|
628
|
+
AWS_DISCOVERY_KMS_KEY_ID:
|
|
629
|
+
discoveredResources.defaultKmsKeyId,
|
|
630
|
+
}),
|
|
516
631
|
},
|
|
517
632
|
iamRoleStatements: [
|
|
518
633
|
{
|
|
@@ -528,22 +643,22 @@ const composeServerlessDefinition = async (AppDefinition) => {
|
|
|
528
643
|
'sqs:SendMessage',
|
|
529
644
|
'sqs:SendMessageBatch',
|
|
530
645
|
'sqs:GetQueueUrl',
|
|
531
|
-
'sqs:GetQueueAttributes'
|
|
646
|
+
'sqs:GetQueueAttributes',
|
|
532
647
|
],
|
|
533
648
|
Resource: [
|
|
534
649
|
{
|
|
535
|
-
'Fn::GetAtt': ['InternalErrorQueue', 'Arn']
|
|
650
|
+
'Fn::GetAtt': ['InternalErrorQueue', 'Arn'],
|
|
536
651
|
},
|
|
537
652
|
{
|
|
538
653
|
'Fn::Join': [
|
|
539
654
|
':',
|
|
540
655
|
[
|
|
541
|
-
'arn:aws:sqs:${self:provider.region}:*:${self:service}--${self:provider.stage}-*Queue'
|
|
542
|
-
]
|
|
543
|
-
]
|
|
544
|
-
}
|
|
656
|
+
'arn:aws:sqs:${self:provider.region}:*:${self:service}--${self:provider.stage}-*Queue',
|
|
657
|
+
],
|
|
658
|
+
],
|
|
659
|
+
},
|
|
545
660
|
],
|
|
546
|
-
}
|
|
661
|
+
},
|
|
547
662
|
],
|
|
548
663
|
httpApi: {
|
|
549
664
|
payload: '2.0',
|
|
@@ -555,7 +670,7 @@ const composeServerlessDefinition = async (AppDefinition) => {
|
|
|
555
670
|
},
|
|
556
671
|
name: '${opt:stage, "dev"}-${self:service}',
|
|
557
672
|
disableDefaultEndpoint: false,
|
|
558
|
-
}
|
|
673
|
+
},
|
|
559
674
|
},
|
|
560
675
|
plugins: [
|
|
561
676
|
'serverless-jetpack',
|
|
@@ -585,7 +700,8 @@ const composeServerlessDefinition = async (AppDefinition) => {
|
|
|
585
700
|
},
|
|
586
701
|
functions: {
|
|
587
702
|
auth: {
|
|
588
|
-
handler:
|
|
703
|
+
handler:
|
|
704
|
+
'node_modules/@friggframework/core/handlers/routers/auth.handler',
|
|
589
705
|
events: [
|
|
590
706
|
{
|
|
591
707
|
httpApi: {
|
|
@@ -608,7 +724,8 @@ const composeServerlessDefinition = async (AppDefinition) => {
|
|
|
608
724
|
],
|
|
609
725
|
},
|
|
610
726
|
user: {
|
|
611
|
-
handler:
|
|
727
|
+
handler:
|
|
728
|
+
'node_modules/@friggframework/core/handlers/routers/user.handler',
|
|
612
729
|
events: [
|
|
613
730
|
{
|
|
614
731
|
httpApi: {
|
|
@@ -619,7 +736,8 @@ const composeServerlessDefinition = async (AppDefinition) => {
|
|
|
619
736
|
],
|
|
620
737
|
},
|
|
621
738
|
health: {
|
|
622
|
-
handler:
|
|
739
|
+
handler:
|
|
740
|
+
'node_modules/@friggframework/core/handlers/routers/health.handler',
|
|
623
741
|
events: [
|
|
624
742
|
{
|
|
625
743
|
httpApi: {
|
|
@@ -723,23 +841,28 @@ const composeServerlessDefinition = async (AppDefinition) => {
|
|
|
723
841
|
};
|
|
724
842
|
|
|
725
843
|
// KMS Configuration based on App Definition
|
|
726
|
-
if (
|
|
844
|
+
if (
|
|
845
|
+
AppDefinition.encryption?.useDefaultKMSForFieldLevelEncryption === true
|
|
846
|
+
) {
|
|
727
847
|
// Check if a KMS key was discovered
|
|
728
848
|
if (discoveredResources.defaultKmsKeyId) {
|
|
729
849
|
// Use the existing discovered KMS key
|
|
730
|
-
console.log(
|
|
731
|
-
|
|
850
|
+
console.log(
|
|
851
|
+
`Using existing KMS key: ${discoveredResources.defaultKmsKeyId}`
|
|
852
|
+
);
|
|
853
|
+
|
|
732
854
|
definition.provider.iamRoleStatements.push({
|
|
733
855
|
Effect: 'Allow',
|
|
734
856
|
Action: ['kms:GenerateDataKey', 'kms:Decrypt'],
|
|
735
|
-
Resource: [discoveredResources.defaultKmsKeyId]
|
|
857
|
+
Resource: [discoveredResources.defaultKmsKeyId],
|
|
736
858
|
});
|
|
737
859
|
|
|
738
|
-
definition.provider.environment.KMS_KEY_ARN =
|
|
860
|
+
definition.provider.environment.KMS_KEY_ARN =
|
|
861
|
+
discoveredResources.defaultKmsKeyId;
|
|
739
862
|
} else {
|
|
740
863
|
// No existing key found, provision a dedicated KMS key
|
|
741
864
|
console.log('No existing KMS key found, creating a new one...');
|
|
742
|
-
|
|
865
|
+
|
|
743
866
|
definition.resources.Resources.FriggKMSKey = {
|
|
744
867
|
Type: 'AWS::KMS::Key',
|
|
745
868
|
Properties: {
|
|
@@ -750,29 +873,38 @@ const composeServerlessDefinition = async (AppDefinition) => {
|
|
|
750
873
|
{
|
|
751
874
|
Sid: 'AllowRootAccountAdmin',
|
|
752
875
|
Effect: 'Allow',
|
|
753
|
-
Principal: {
|
|
876
|
+
Principal: {
|
|
877
|
+
AWS: {
|
|
878
|
+
'Fn::Sub':
|
|
879
|
+
'arn:aws:iam::${AWS::AccountId}:root',
|
|
880
|
+
},
|
|
881
|
+
},
|
|
754
882
|
Action: 'kms:*',
|
|
755
|
-
Resource: '*'
|
|
756
|
-
}
|
|
757
|
-
]
|
|
758
|
-
}
|
|
759
|
-
}
|
|
883
|
+
Resource: '*',
|
|
884
|
+
},
|
|
885
|
+
],
|
|
886
|
+
},
|
|
887
|
+
},
|
|
760
888
|
};
|
|
761
889
|
|
|
762
890
|
definition.provider.iamRoleStatements.push({
|
|
763
891
|
Effect: 'Allow',
|
|
764
892
|
Action: ['kms:GenerateDataKey', 'kms:Decrypt'],
|
|
765
|
-
Resource: [{ 'Fn::GetAtt': ['FriggKMSKey', 'Arn'] }]
|
|
893
|
+
Resource: [{ 'Fn::GetAtt': ['FriggKMSKey', 'Arn'] }],
|
|
766
894
|
});
|
|
767
895
|
|
|
768
|
-
definition.provider.environment.KMS_KEY_ARN = {
|
|
896
|
+
definition.provider.environment.KMS_KEY_ARN = {
|
|
897
|
+
'Fn::GetAtt': ['FriggKMSKey', 'Arn'],
|
|
898
|
+
};
|
|
769
899
|
}
|
|
770
900
|
|
|
771
901
|
definition.plugins.push('serverless-kms-grants');
|
|
772
902
|
|
|
773
903
|
// Configure KMS grants with discovered default key or environment variable
|
|
774
904
|
definition.custom.kmsGrants = {
|
|
775
|
-
kmsKeyId:
|
|
905
|
+
kmsKeyId:
|
|
906
|
+
discoveredResources.defaultKmsKeyId ||
|
|
907
|
+
'${env:AWS_DISCOVERY_KMS_KEY_ID}',
|
|
776
908
|
};
|
|
777
909
|
}
|
|
778
910
|
|
|
@@ -786,9 +918,9 @@ const composeServerlessDefinition = async (AppDefinition) => {
|
|
|
786
918
|
'ec2:DescribeNetworkInterfaces',
|
|
787
919
|
'ec2:DeleteNetworkInterface',
|
|
788
920
|
'ec2:AttachNetworkInterface',
|
|
789
|
-
'ec2:DetachNetworkInterface'
|
|
921
|
+
'ec2:DetachNetworkInterface',
|
|
790
922
|
],
|
|
791
|
-
Resource: '*'
|
|
923
|
+
Resource: '*',
|
|
792
924
|
});
|
|
793
925
|
|
|
794
926
|
// Default approach: Use AWS Discovery to find existing VPC resources
|
|
@@ -801,7 +933,9 @@ const composeServerlessDefinition = async (AppDefinition) => {
|
|
|
801
933
|
vpcConfig.securityGroupIds = AppDefinition.vpc.securityGroupIds;
|
|
802
934
|
} else {
|
|
803
935
|
// Use auto-created security group
|
|
804
|
-
vpcConfig.securityGroupIds = [
|
|
936
|
+
vpcConfig.securityGroupIds = [
|
|
937
|
+
{ Ref: 'FriggLambdaSecurityGroup' },
|
|
938
|
+
];
|
|
805
939
|
}
|
|
806
940
|
|
|
807
941
|
if (AppDefinition.vpc.subnetIds) {
|
|
@@ -811,7 +945,7 @@ const composeServerlessDefinition = async (AppDefinition) => {
|
|
|
811
945
|
// Use auto-created private subnets
|
|
812
946
|
vpcConfig.subnetIds = [
|
|
813
947
|
{ Ref: 'FriggPrivateSubnet1' },
|
|
814
|
-
{ Ref: 'FriggPrivateSubnet2' }
|
|
948
|
+
{ Ref: 'FriggPrivateSubnet2' },
|
|
815
949
|
];
|
|
816
950
|
}
|
|
817
951
|
|
|
@@ -825,16 +959,27 @@ const composeServerlessDefinition = async (AppDefinition) => {
|
|
|
825
959
|
// Option 2: Use AWS Discovery (default behavior)
|
|
826
960
|
// VPC configuration using discovered or explicitly provided resources
|
|
827
961
|
const vpcConfig = {
|
|
828
|
-
securityGroupIds:
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
[
|
|
833
|
-
|
|
962
|
+
securityGroupIds:
|
|
963
|
+
AppDefinition.vpc.securityGroupIds ||
|
|
964
|
+
(discoveredResources.defaultSecurityGroupId
|
|
965
|
+
? [discoveredResources.defaultSecurityGroupId]
|
|
966
|
+
: []),
|
|
967
|
+
subnetIds:
|
|
968
|
+
AppDefinition.vpc.subnetIds ||
|
|
969
|
+
(discoveredResources.privateSubnetId1 &&
|
|
970
|
+
discoveredResources.privateSubnetId2
|
|
971
|
+
? [
|
|
972
|
+
discoveredResources.privateSubnetId1,
|
|
973
|
+
discoveredResources.privateSubnetId2,
|
|
974
|
+
]
|
|
975
|
+
: []),
|
|
834
976
|
};
|
|
835
977
|
|
|
836
978
|
// Set VPC config for Lambda functions only if we have valid subnet IDs
|
|
837
|
-
if (
|
|
979
|
+
if (
|
|
980
|
+
vpcConfig.subnetIds.length >= 2 &&
|
|
981
|
+
vpcConfig.securityGroupIds.length > 0
|
|
982
|
+
) {
|
|
838
983
|
definition.provider.vpc = vpcConfig;
|
|
839
984
|
|
|
840
985
|
// Check if we have an existing NAT Gateway to use
|
|
@@ -848,22 +993,35 @@ const composeServerlessDefinition = async (AppDefinition) => {
|
|
|
848
993
|
Properties: {
|
|
849
994
|
Domain: 'vpc',
|
|
850
995
|
Tags: [
|
|
851
|
-
{
|
|
852
|
-
|
|
853
|
-
|
|
996
|
+
{
|
|
997
|
+
Key: 'Name',
|
|
998
|
+
Value: '${self:service}-${self:provider.stage}-nat-eip',
|
|
999
|
+
},
|
|
1000
|
+
],
|
|
1001
|
+
},
|
|
854
1002
|
};
|
|
855
1003
|
}
|
|
856
1004
|
|
|
857
1005
|
definition.resources.Resources.FriggNATGateway = {
|
|
858
1006
|
Type: 'AWS::EC2::NatGateway',
|
|
859
1007
|
Properties: {
|
|
860
|
-
AllocationId:
|
|
861
|
-
|
|
862
|
-
|
|
1008
|
+
AllocationId:
|
|
1009
|
+
discoveredResources.existingElasticIpAllocationId || {
|
|
1010
|
+
'Fn::GetAtt': [
|
|
1011
|
+
'FriggNATGatewayEIP',
|
|
1012
|
+
'AllocationId',
|
|
1013
|
+
],
|
|
1014
|
+
},
|
|
1015
|
+
SubnetId:
|
|
1016
|
+
discoveredResources.publicSubnetId ||
|
|
1017
|
+
discoveredResources.privateSubnetId1, // Use first discovered subnet if no public subnet found
|
|
863
1018
|
Tags: [
|
|
864
|
-
{
|
|
865
|
-
|
|
866
|
-
|
|
1019
|
+
{
|
|
1020
|
+
Key: 'Name',
|
|
1021
|
+
Value: '${self:service}-${self:provider.stage}-nat-gateway',
|
|
1022
|
+
},
|
|
1023
|
+
],
|
|
1024
|
+
},
|
|
867
1025
|
};
|
|
868
1026
|
}
|
|
869
1027
|
|
|
@@ -871,11 +1029,16 @@ const composeServerlessDefinition = async (AppDefinition) => {
|
|
|
871
1029
|
definition.resources.Resources.FriggLambdaRouteTable = {
|
|
872
1030
|
Type: 'AWS::EC2::RouteTable',
|
|
873
1031
|
Properties: {
|
|
874
|
-
VpcId: discoveredResources.defaultVpcId || {
|
|
1032
|
+
VpcId: discoveredResources.defaultVpcId || {
|
|
1033
|
+
Ref: 'FriggVPC',
|
|
1034
|
+
},
|
|
875
1035
|
Tags: [
|
|
876
|
-
{
|
|
877
|
-
|
|
878
|
-
|
|
1036
|
+
{
|
|
1037
|
+
Key: 'Name',
|
|
1038
|
+
Value: '${self:service}-${self:provider.stage}-lambda-rt',
|
|
1039
|
+
},
|
|
1040
|
+
],
|
|
1041
|
+
},
|
|
879
1042
|
};
|
|
880
1043
|
|
|
881
1044
|
definition.resources.Resources.FriggNATRoute = {
|
|
@@ -883,8 +1046,11 @@ const composeServerlessDefinition = async (AppDefinition) => {
|
|
|
883
1046
|
Properties: {
|
|
884
1047
|
RouteTableId: { Ref: 'FriggLambdaRouteTable' },
|
|
885
1048
|
DestinationCidrBlock: '0.0.0.0/0',
|
|
886
|
-
NatGatewayId:
|
|
887
|
-
|
|
1049
|
+
NatGatewayId:
|
|
1050
|
+
discoveredResources.existingNatGatewayId || {
|
|
1051
|
+
Ref: 'FriggNATGateway',
|
|
1052
|
+
},
|
|
1053
|
+
},
|
|
888
1054
|
};
|
|
889
1055
|
|
|
890
1056
|
// Associate Lambda subnets with NAT Gateway route table
|
|
@@ -892,16 +1058,16 @@ const composeServerlessDefinition = async (AppDefinition) => {
|
|
|
892
1058
|
Type: 'AWS::EC2::SubnetRouteTableAssociation',
|
|
893
1059
|
Properties: {
|
|
894
1060
|
SubnetId: vpcConfig.subnetIds[0],
|
|
895
|
-
RouteTableId: { Ref: 'FriggLambdaRouteTable' }
|
|
896
|
-
}
|
|
1061
|
+
RouteTableId: { Ref: 'FriggLambdaRouteTable' },
|
|
1062
|
+
},
|
|
897
1063
|
};
|
|
898
1064
|
|
|
899
1065
|
definition.resources.Resources.FriggSubnet2RouteAssociation = {
|
|
900
1066
|
Type: 'AWS::EC2::SubnetRouteTableAssociation',
|
|
901
1067
|
Properties: {
|
|
902
1068
|
SubnetId: vpcConfig.subnetIds[1],
|
|
903
|
-
RouteTableId: { Ref: 'FriggLambdaRouteTable' }
|
|
904
|
-
}
|
|
1069
|
+
RouteTableId: { Ref: 'FriggLambdaRouteTable' },
|
|
1070
|
+
},
|
|
905
1071
|
};
|
|
906
1072
|
|
|
907
1073
|
// Add VPC endpoints for AWS service optimization (optional but recommended)
|
|
@@ -910,116 +1076,130 @@ const composeServerlessDefinition = async (AppDefinition) => {
|
|
|
910
1076
|
Type: 'AWS::EC2::VPCEndpoint',
|
|
911
1077
|
Properties: {
|
|
912
1078
|
VpcId: discoveredResources.defaultVpcId,
|
|
913
|
-
ServiceName:
|
|
1079
|
+
ServiceName:
|
|
1080
|
+
'com.amazonaws.${self:provider.region}.s3',
|
|
914
1081
|
VpcEndpointType: 'Gateway',
|
|
915
|
-
RouteTableIds: [{ Ref: 'FriggLambdaRouteTable' }]
|
|
916
|
-
}
|
|
1082
|
+
RouteTableIds: [{ Ref: 'FriggLambdaRouteTable' }],
|
|
1083
|
+
},
|
|
917
1084
|
};
|
|
918
1085
|
|
|
919
1086
|
definition.resources.Resources.VPCEndpointDynamoDB = {
|
|
920
1087
|
Type: 'AWS::EC2::VPCEndpoint',
|
|
921
1088
|
Properties: {
|
|
922
1089
|
VpcId: discoveredResources.defaultVpcId,
|
|
923
|
-
ServiceName:
|
|
1090
|
+
ServiceName:
|
|
1091
|
+
'com.amazonaws.${self:provider.region}.dynamodb',
|
|
924
1092
|
VpcEndpointType: 'Gateway',
|
|
925
|
-
RouteTableIds: [{ Ref: 'FriggLambdaRouteTable' }]
|
|
926
|
-
}
|
|
1093
|
+
RouteTableIds: [{ Ref: 'FriggLambdaRouteTable' }],
|
|
1094
|
+
},
|
|
927
1095
|
};
|
|
928
1096
|
}
|
|
929
1097
|
}
|
|
930
1098
|
}
|
|
931
1099
|
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
1100
|
+
// SSM Parameter Store Configuration based on App Definition
|
|
1101
|
+
if (AppDefinition.ssm?.enable === true) {
|
|
1102
|
+
// Add AWS Parameters and Secrets Lambda Extension layer
|
|
1103
|
+
definition.provider.layers = [
|
|
1104
|
+
'arn:aws:lambda:${self:provider.region}:177933569100:layer:AWS-Parameters-and-Secrets-Lambda-Extension:11',
|
|
1105
|
+
];
|
|
938
1106
|
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
1107
|
+
// Add SSM IAM permissions
|
|
1108
|
+
definition.provider.iamRoleStatements.push({
|
|
1109
|
+
Effect: 'Allow',
|
|
1110
|
+
Action: [
|
|
1111
|
+
'ssm:GetParameter',
|
|
1112
|
+
'ssm:GetParameters',
|
|
1113
|
+
'ssm:GetParametersByPath',
|
|
1114
|
+
],
|
|
1115
|
+
Resource: [
|
|
1116
|
+
'arn:aws:ssm:${self:provider.region}:*:parameter/${self:service}/${self:provider.stage}/*',
|
|
1117
|
+
],
|
|
1118
|
+
});
|
|
951
1119
|
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
1120
|
+
// Add environment variable for SSM parameter prefix
|
|
1121
|
+
definition.provider.environment.SSM_PARAMETER_PREFIX =
|
|
1122
|
+
'/${self:service}/${self:provider.stage}';
|
|
1123
|
+
}
|
|
955
1124
|
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
1125
|
+
// Add integration-specific functions and resources
|
|
1126
|
+
if (
|
|
1127
|
+
AppDefinition.integrations &&
|
|
1128
|
+
Array.isArray(AppDefinition.integrations)
|
|
1129
|
+
) {
|
|
1130
|
+
for (const integration of AppDefinition.integrations) {
|
|
1131
|
+
if (
|
|
1132
|
+
!integration ||
|
|
1133
|
+
!integration.Definition ||
|
|
1134
|
+
!integration.Definition.name
|
|
1135
|
+
) {
|
|
1136
|
+
throw new Error(
|
|
1137
|
+
'Invalid integration: missing Definition or name'
|
|
1138
|
+
);
|
|
1139
|
+
}
|
|
1140
|
+
const integrationName = integration.Definition.name;
|
|
963
1141
|
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
1142
|
+
// Add function for the integration
|
|
1143
|
+
definition.functions[integrationName] = {
|
|
1144
|
+
handler: `node_modules/@friggframework/core/handlers/routers/integration-defined-routers.handlers.${integrationName}.handler`,
|
|
1145
|
+
events: [
|
|
1146
|
+
{
|
|
1147
|
+
httpApi: {
|
|
1148
|
+
path: `/api/${integrationName}-integration/{proxy+}`,
|
|
1149
|
+
method: 'ANY',
|
|
1150
|
+
},
|
|
972
1151
|
},
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
};
|
|
1152
|
+
],
|
|
1153
|
+
};
|
|
976
1154
|
|
|
977
|
-
|
|
978
|
-
|
|
1155
|
+
// Add SQS Queue for the integration
|
|
1156
|
+
const queueReference = `${
|
|
1157
|
+
integrationName.charAt(0).toUpperCase() +
|
|
1158
|
+
integrationName.slice(1)
|
|
979
1159
|
}Queue`;
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
1160
|
+
const queueName = `\${self:service}--\${self:provider.stage}-${queueReference}`;
|
|
1161
|
+
definition.resources.Resources[queueReference] = {
|
|
1162
|
+
Type: 'AWS::SQS::Queue',
|
|
1163
|
+
Properties: {
|
|
1164
|
+
QueueName: `\${self:custom.${queueReference}}`,
|
|
1165
|
+
MessageRetentionPeriod: 60,
|
|
1166
|
+
VisibilityTimeout: 1800, // 30 minutes
|
|
1167
|
+
RedrivePolicy: {
|
|
1168
|
+
maxReceiveCount: 1,
|
|
1169
|
+
deadLetterTargetArn: {
|
|
1170
|
+
'Fn::GetAtt': ['InternalErrorQueue', 'Arn'],
|
|
1171
|
+
},
|
|
991
1172
|
},
|
|
992
1173
|
},
|
|
993
|
-
}
|
|
994
|
-
};
|
|
1174
|
+
};
|
|
995
1175
|
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1176
|
+
// Add Queue Worker for the integration
|
|
1177
|
+
const queueWorkerName = `${integrationName}QueueWorker`;
|
|
1178
|
+
definition.functions[queueWorkerName] = {
|
|
1179
|
+
handler: `node_modules/@friggframework/core/handlers/workers/integration-defined-workers.handlers.${integrationName}.queueWorker`,
|
|
1180
|
+
reservedConcurrency: 5,
|
|
1181
|
+
events: [
|
|
1182
|
+
{
|
|
1183
|
+
sqs: {
|
|
1184
|
+
arn: {
|
|
1185
|
+
'Fn::GetAtt': [queueReference, 'Arn'],
|
|
1186
|
+
},
|
|
1187
|
+
batchSize: 1,
|
|
1006
1188
|
},
|
|
1007
|
-
batchSize: 1,
|
|
1008
1189
|
},
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
};
|
|
1190
|
+
],
|
|
1191
|
+
timeout: 600,
|
|
1192
|
+
};
|
|
1013
1193
|
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1194
|
+
// Add Queue URL for the integration to the ENVironment variables
|
|
1195
|
+
definition.provider.environment = {
|
|
1196
|
+
...definition.provider.environment,
|
|
1197
|
+
[`${integrationName.toUpperCase()}_QUEUE_URL`]: {
|
|
1198
|
+
Ref: queueReference,
|
|
1199
|
+
},
|
|
1200
|
+
};
|
|
1021
1201
|
|
|
1022
|
-
|
|
1202
|
+
definition.custom[queueReference] = queueName;
|
|
1023
1203
|
}
|
|
1024
1204
|
}
|
|
1025
1205
|
|
|
@@ -1029,7 +1209,8 @@ const composeServerlessDefinition = async (AppDefinition) => {
|
|
|
1029
1209
|
// Add websocket function if enabled
|
|
1030
1210
|
if (AppDefinition.websockets?.enable === true) {
|
|
1031
1211
|
definition.functions.defaultWebsocket = {
|
|
1032
|
-
handler:
|
|
1212
|
+
handler:
|
|
1213
|
+
'node_modules/@friggframework/core/handlers/routers/websocket.handler',
|
|
1033
1214
|
events: [
|
|
1034
1215
|
{
|
|
1035
1216
|
websocket: {
|
|
@@ -1056,7 +1237,8 @@ const composeServerlessDefinition = async (AppDefinition) => {
|
|
|
1056
1237
|
// Add websocket function if enabled
|
|
1057
1238
|
if (AppDefinition.websockets?.enable === true) {
|
|
1058
1239
|
definition.functions.defaultWebsocket = {
|
|
1059
|
-
handler:
|
|
1240
|
+
handler:
|
|
1241
|
+
'node_modules/@friggframework/core/handlers/routers/websocket.handler',
|
|
1060
1242
|
events: [
|
|
1061
1243
|
{
|
|
1062
1244
|
websocket: {
|
|
@@ -1084,7 +1266,8 @@ const composeServerlessDefinition = async (AppDefinition) => {
|
|
|
1084
1266
|
// Add websocket function if enabled
|
|
1085
1267
|
if (AppDefinition.websockets?.enable === true) {
|
|
1086
1268
|
definition.functions.defaultWebsocket = {
|
|
1087
|
-
handler:
|
|
1269
|
+
handler:
|
|
1270
|
+
'node_modules/@friggframework/core/handlers/routers/websocket.handler',
|
|
1088
1271
|
events: [
|
|
1089
1272
|
{
|
|
1090
1273
|
websocket: {
|
|
@@ -1114,4 +1297,4 @@ const composeServerlessDefinition = async (AppDefinition) => {
|
|
|
1114
1297
|
return definition;
|
|
1115
1298
|
};
|
|
1116
1299
|
|
|
1117
|
-
module.exports = { composeServerlessDefinition };
|
|
1300
|
+
module.exports = { composeServerlessDefinition };
|