@friggframework/devtools 2.0.0--canary.406.78e2685.0 → 2.0.0--canary.398.24926ac.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/frigg-cli/build-command/index.js +4 -2
- package/frigg-cli/deploy-command/index.js +5 -2
- package/frigg-cli/generate-iam-command.js +115 -0
- package/frigg-cli/index.js +11 -1
- package/infrastructure/AWS-DISCOVERY-TROUBLESHOOTING.md +245 -0
- package/infrastructure/AWS-IAM-CREDENTIAL-NEEDS.md +596 -0
- package/infrastructure/DEPLOYMENT-INSTRUCTIONS.md +268 -0
- package/infrastructure/GENERATE-IAM-DOCS.md +253 -0
- package/infrastructure/IAM-POLICY-TEMPLATES.md +176 -0
- package/infrastructure/README-TESTING.md +332 -0
- package/infrastructure/README.md +421 -0
- package/infrastructure/WEBSOCKET-CONFIGURATION.md +105 -0
- package/infrastructure/__tests__/fixtures/mock-aws-resources.js +391 -0
- package/infrastructure/__tests__/helpers/test-utils.js +277 -0
- package/infrastructure/aws-discovery.js +568 -0
- package/infrastructure/aws-discovery.test.js +373 -0
- package/infrastructure/build-time-discovery.js +206 -0
- package/infrastructure/build-time-discovery.test.js +375 -0
- package/infrastructure/create-frigg-infrastructure.js +2 -2
- package/infrastructure/frigg-deployment-iam-stack.yaml +379 -0
- package/infrastructure/iam-generator.js +687 -0
- package/infrastructure/iam-generator.test.js +169 -0
- package/infrastructure/iam-policy-basic.json +212 -0
- package/infrastructure/iam-policy-full.json +282 -0
- package/infrastructure/integration.test.js +383 -0
- package/infrastructure/run-discovery.js +110 -0
- package/infrastructure/serverless-template.js +514 -167
- package/infrastructure/serverless-template.test.js +541 -0
- package/management-ui/dist/assets/FriggLogo-B7Xx8ZW1.svg +1 -0
- package/management-ui/dist/assets/index-BA21WgFa.js +1221 -0
- package/management-ui/dist/assets/index-CbM64Oba.js +1221 -0
- package/management-ui/dist/assets/index-CkvseXTC.css +1 -0
- package/management-ui/dist/index.html +14 -0
- package/package.json +9 -5
|
@@ -0,0 +1,687 @@
|
|
|
1
|
+
const fs = require('fs-extra');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Generate IAM CloudFormation template based on AppDefinition
|
|
6
|
+
* @param {Object} appDefinition - Application definition object
|
|
7
|
+
* @param {Object} options - Generation options
|
|
8
|
+
* @param {string} [options.deploymentUserName='frigg-deployment-user'] - IAM user name
|
|
9
|
+
* @param {string} [options.stackName='frigg-deployment-iam'] - CloudFormation stack name
|
|
10
|
+
* @param {string} [options.mode='auto'] - Policy mode: 'basic', 'full', or 'auto' (auto-detect from appDefinition)
|
|
11
|
+
* @returns {string} CloudFormation YAML template
|
|
12
|
+
*/
|
|
13
|
+
function generateIAMCloudFormation(appDefinition, options = {}) {
|
|
14
|
+
const {
|
|
15
|
+
deploymentUserName = 'frigg-deployment-user',
|
|
16
|
+
stackName = 'frigg-deployment-iam',
|
|
17
|
+
mode = 'auto'
|
|
18
|
+
} = options;
|
|
19
|
+
|
|
20
|
+
// Determine which features are enabled based on mode
|
|
21
|
+
let features;
|
|
22
|
+
if (mode === 'basic') {
|
|
23
|
+
features = {
|
|
24
|
+
vpc: false,
|
|
25
|
+
kms: false,
|
|
26
|
+
ssm: false,
|
|
27
|
+
websockets: appDefinition.websockets?.enable === true
|
|
28
|
+
};
|
|
29
|
+
} else if (mode === 'full') {
|
|
30
|
+
features = {
|
|
31
|
+
vpc: true,
|
|
32
|
+
kms: true,
|
|
33
|
+
ssm: true,
|
|
34
|
+
websockets: appDefinition.websockets?.enable === true
|
|
35
|
+
};
|
|
36
|
+
} else { // mode === 'auto'
|
|
37
|
+
features = {
|
|
38
|
+
vpc: appDefinition.vpc?.enable === true,
|
|
39
|
+
kms: appDefinition.encryption?.useDefaultKMSForFieldLevelEncryption === true,
|
|
40
|
+
ssm: appDefinition.ssm?.enable === true,
|
|
41
|
+
websockets: appDefinition.websockets?.enable === true
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Build the CloudFormation template
|
|
46
|
+
const template = {
|
|
47
|
+
AWSTemplateFormatVersion: '2010-09-09',
|
|
48
|
+
Description: `IAM roles and policies for ${appDefinition.name || 'Frigg'} application deployment pipeline`,
|
|
49
|
+
|
|
50
|
+
Parameters: {
|
|
51
|
+
DeploymentUserName: {
|
|
52
|
+
Type: 'String',
|
|
53
|
+
Default: deploymentUserName,
|
|
54
|
+
Description: 'Name for the IAM user that will deploy Frigg applications'
|
|
55
|
+
},
|
|
56
|
+
EnableVPCSupport: {
|
|
57
|
+
Type: 'String',
|
|
58
|
+
Default: features.vpc ? 'true' : 'false',
|
|
59
|
+
AllowedValues: ['true', 'false'],
|
|
60
|
+
Description: 'Enable VPC-related permissions for Frigg applications'
|
|
61
|
+
},
|
|
62
|
+
EnableKMSSupport: {
|
|
63
|
+
Type: 'String',
|
|
64
|
+
Default: features.kms ? 'true' : 'false',
|
|
65
|
+
AllowedValues: ['true', 'false'],
|
|
66
|
+
Description: 'Enable KMS encryption permissions for Frigg applications'
|
|
67
|
+
},
|
|
68
|
+
EnableSSMSupport: {
|
|
69
|
+
Type: 'String',
|
|
70
|
+
Default: features.ssm ? 'true' : 'false',
|
|
71
|
+
AllowedValues: ['true', 'false'],
|
|
72
|
+
Description: 'Enable SSM Parameter Store permissions for Frigg applications'
|
|
73
|
+
}
|
|
74
|
+
},
|
|
75
|
+
|
|
76
|
+
Conditions: {
|
|
77
|
+
CreateVPCPermissions: { 'Fn::Equals': [{ Ref: 'EnableVPCSupport' }, 'true'] },
|
|
78
|
+
CreateKMSPermissions: { 'Fn::Equals': [{ Ref: 'EnableKMSSupport' }, 'true'] },
|
|
79
|
+
CreateSSMPermissions: { 'Fn::Equals': [{ Ref: 'EnableSSMSupport' }, 'true'] }
|
|
80
|
+
},
|
|
81
|
+
|
|
82
|
+
Resources: {}
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
// Add IAM User
|
|
86
|
+
template.Resources.FriggDeploymentUser = {
|
|
87
|
+
Type: 'AWS::IAM::User',
|
|
88
|
+
Properties: {
|
|
89
|
+
UserName: { Ref: 'DeploymentUserName' },
|
|
90
|
+
ManagedPolicyArns: [
|
|
91
|
+
{ Ref: 'FriggDiscoveryPolicy' },
|
|
92
|
+
{ Ref: 'FriggCoreDeploymentPolicy' }
|
|
93
|
+
]
|
|
94
|
+
}
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
// Conditionally add feature-specific policies
|
|
98
|
+
if (features.vpc) {
|
|
99
|
+
template.Resources.FriggDeploymentUser.Properties.ManagedPolicyArns.push({
|
|
100
|
+
'Fn::If': ['CreateVPCPermissions', { Ref: 'FriggVPCPolicy' }, { Ref: 'AWS::NoValue' }]
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
if (features.kms) {
|
|
104
|
+
template.Resources.FriggDeploymentUser.Properties.ManagedPolicyArns.push({
|
|
105
|
+
'Fn::If': ['CreateKMSPermissions', { Ref: 'FriggKMSPolicy' }, { Ref: 'AWS::NoValue' }]
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
if (features.ssm) {
|
|
109
|
+
template.Resources.FriggDeploymentUser.Properties.ManagedPolicyArns.push({
|
|
110
|
+
'Fn::If': ['CreateSSMPermissions', { Ref: 'FriggSSMPolicy' }, { Ref: 'AWS::NoValue' }]
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// Add Access Key
|
|
115
|
+
template.Resources.FriggDeploymentAccessKey = {
|
|
116
|
+
Type: 'AWS::IAM::AccessKey',
|
|
117
|
+
Properties: {
|
|
118
|
+
UserName: { Ref: 'FriggDeploymentUser' }
|
|
119
|
+
}
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
// Add Discovery Policy (always needed)
|
|
123
|
+
template.Resources.FriggDiscoveryPolicy = {
|
|
124
|
+
Type: 'AWS::IAM::ManagedPolicy',
|
|
125
|
+
Properties: {
|
|
126
|
+
ManagedPolicyName: 'FriggDiscoveryPolicy',
|
|
127
|
+
Description: 'Permissions for AWS resource discovery during Frigg build process',
|
|
128
|
+
PolicyDocument: {
|
|
129
|
+
Version: '2012-10-17',
|
|
130
|
+
Statement: [
|
|
131
|
+
{
|
|
132
|
+
Sid: 'AWSDiscoveryPermissions',
|
|
133
|
+
Effect: 'Allow',
|
|
134
|
+
Action: [
|
|
135
|
+
'sts:GetCallerIdentity',
|
|
136
|
+
'ec2:DescribeVpcs',
|
|
137
|
+
'ec2:DescribeSubnets',
|
|
138
|
+
'ec2:DescribeSecurityGroups',
|
|
139
|
+
'ec2:DescribeRouteTables',
|
|
140
|
+
'ec2:DescribeNatGateways',
|
|
141
|
+
'ec2:DescribeAddresses',
|
|
142
|
+
'kms:ListKeys',
|
|
143
|
+
'kms:DescribeKey'
|
|
144
|
+
],
|
|
145
|
+
Resource: '*'
|
|
146
|
+
}
|
|
147
|
+
]
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
};
|
|
151
|
+
|
|
152
|
+
// Add Core Deployment Policy (always needed)
|
|
153
|
+
const coreActions = [
|
|
154
|
+
// CloudFormation permissions
|
|
155
|
+
'cloudformation:CreateStack',
|
|
156
|
+
'cloudformation:UpdateStack',
|
|
157
|
+
'cloudformation:DeleteStack',
|
|
158
|
+
'cloudformation:DescribeStacks',
|
|
159
|
+
'cloudformation:DescribeStackEvents',
|
|
160
|
+
'cloudformation:DescribeStackResources',
|
|
161
|
+
'cloudformation:DescribeStackResource',
|
|
162
|
+
'cloudformation:ListStackResources',
|
|
163
|
+
'cloudformation:GetTemplate',
|
|
164
|
+
'cloudformation:DescribeChangeSet',
|
|
165
|
+
'cloudformation:CreateChangeSet',
|
|
166
|
+
'cloudformation:DeleteChangeSet',
|
|
167
|
+
'cloudformation:ExecuteChangeSet',
|
|
168
|
+
'cloudformation:ValidateTemplate',
|
|
169
|
+
|
|
170
|
+
// Lambda permissions
|
|
171
|
+
'lambda:CreateFunction',
|
|
172
|
+
'lambda:UpdateFunctionCode',
|
|
173
|
+
'lambda:UpdateFunctionConfiguration',
|
|
174
|
+
'lambda:DeleteFunction',
|
|
175
|
+
'lambda:GetFunction',
|
|
176
|
+
'lambda:ListFunctions',
|
|
177
|
+
'lambda:PublishVersion',
|
|
178
|
+
'lambda:CreateAlias',
|
|
179
|
+
'lambda:UpdateAlias',
|
|
180
|
+
'lambda:DeleteAlias',
|
|
181
|
+
'lambda:GetAlias',
|
|
182
|
+
'lambda:AddPermission',
|
|
183
|
+
'lambda:RemovePermission',
|
|
184
|
+
'lambda:GetPolicy',
|
|
185
|
+
'lambda:PutProvisionedConcurrencyConfig',
|
|
186
|
+
'lambda:DeleteProvisionedConcurrencyConfig',
|
|
187
|
+
'lambda:PutConcurrency',
|
|
188
|
+
'lambda:DeleteConcurrency',
|
|
189
|
+
'lambda:TagResource',
|
|
190
|
+
'lambda:UntagResource',
|
|
191
|
+
'lambda:ListVersionsByFunction',
|
|
192
|
+
|
|
193
|
+
// IAM permissions
|
|
194
|
+
'iam:CreateRole',
|
|
195
|
+
'iam:DeleteRole',
|
|
196
|
+
'iam:GetRole',
|
|
197
|
+
'iam:PassRole',
|
|
198
|
+
'iam:PutRolePolicy',
|
|
199
|
+
'iam:DeleteRolePolicy',
|
|
200
|
+
'iam:GetRolePolicy',
|
|
201
|
+
'iam:AttachRolePolicy',
|
|
202
|
+
'iam:DetachRolePolicy',
|
|
203
|
+
'iam:TagRole',
|
|
204
|
+
'iam:UntagRole',
|
|
205
|
+
'iam:ListPolicyVersions',
|
|
206
|
+
|
|
207
|
+
// S3 permissions
|
|
208
|
+
's3:CreateBucket',
|
|
209
|
+
's3:PutObject',
|
|
210
|
+
's3:GetObject',
|
|
211
|
+
's3:DeleteObject',
|
|
212
|
+
's3:PutBucketPolicy',
|
|
213
|
+
's3:PutBucketVersioning',
|
|
214
|
+
's3:PutBucketPublicAccessBlock',
|
|
215
|
+
's3:GetBucketLocation',
|
|
216
|
+
's3:ListBucket',
|
|
217
|
+
|
|
218
|
+
// SQS permissions
|
|
219
|
+
'sqs:CreateQueue',
|
|
220
|
+
'sqs:DeleteQueue',
|
|
221
|
+
'sqs:GetQueueAttributes',
|
|
222
|
+
'sqs:SetQueueAttributes',
|
|
223
|
+
'sqs:GetQueueUrl',
|
|
224
|
+
'sqs:TagQueue',
|
|
225
|
+
'sqs:UntagQueue',
|
|
226
|
+
|
|
227
|
+
// SNS permissions
|
|
228
|
+
'sns:CreateTopic',
|
|
229
|
+
'sns:DeleteTopic',
|
|
230
|
+
'sns:GetTopicAttributes',
|
|
231
|
+
'sns:SetTopicAttributes',
|
|
232
|
+
'sns:Subscribe',
|
|
233
|
+
'sns:Unsubscribe',
|
|
234
|
+
'sns:ListSubscriptionsByTopic',
|
|
235
|
+
'sns:TagResource',
|
|
236
|
+
'sns:UntagResource',
|
|
237
|
+
|
|
238
|
+
// CloudWatch and Logs permissions
|
|
239
|
+
'cloudwatch:PutMetricAlarm',
|
|
240
|
+
'cloudwatch:DeleteAlarms',
|
|
241
|
+
'cloudwatch:DescribeAlarms',
|
|
242
|
+
'logs:CreateLogGroup',
|
|
243
|
+
'logs:CreateLogStream',
|
|
244
|
+
'logs:DeleteLogGroup',
|
|
245
|
+
'logs:DescribeLogGroups',
|
|
246
|
+
'logs:DescribeLogStreams',
|
|
247
|
+
'logs:FilterLogEvents',
|
|
248
|
+
'logs:PutLogEvents',
|
|
249
|
+
'logs:PutRetentionPolicy',
|
|
250
|
+
|
|
251
|
+
// API Gateway permissions
|
|
252
|
+
'apigateway:POST',
|
|
253
|
+
'apigateway:PUT',
|
|
254
|
+
'apigateway:DELETE',
|
|
255
|
+
'apigateway:GET',
|
|
256
|
+
'apigateway:PATCH'
|
|
257
|
+
];
|
|
258
|
+
|
|
259
|
+
const coreStatements = [
|
|
260
|
+
{
|
|
261
|
+
Sid: 'CloudFormationFriggStacks',
|
|
262
|
+
Effect: 'Allow',
|
|
263
|
+
Action: [
|
|
264
|
+
'cloudformation:CreateStack',
|
|
265
|
+
'cloudformation:UpdateStack',
|
|
266
|
+
'cloudformation:DeleteStack',
|
|
267
|
+
'cloudformation:DescribeStacks',
|
|
268
|
+
'cloudformation:DescribeStackEvents',
|
|
269
|
+
'cloudformation:DescribeStackResources',
|
|
270
|
+
'cloudformation:DescribeStackResource',
|
|
271
|
+
'cloudformation:ListStackResources',
|
|
272
|
+
'cloudformation:GetTemplate',
|
|
273
|
+
'cloudformation:DescribeChangeSet',
|
|
274
|
+
'cloudformation:CreateChangeSet',
|
|
275
|
+
'cloudformation:DeleteChangeSet',
|
|
276
|
+
'cloudformation:ExecuteChangeSet'
|
|
277
|
+
],
|
|
278
|
+
Resource: [
|
|
279
|
+
{ 'Fn::Sub': 'arn:aws:cloudformation:*:${AWS::AccountId}:stack/*frigg*/*' }
|
|
280
|
+
]
|
|
281
|
+
},
|
|
282
|
+
{
|
|
283
|
+
Sid: 'CloudFormationValidateTemplate',
|
|
284
|
+
Effect: 'Allow',
|
|
285
|
+
Action: ['cloudformation:ValidateTemplate'],
|
|
286
|
+
Resource: '*'
|
|
287
|
+
},
|
|
288
|
+
{
|
|
289
|
+
Sid: 'S3DeploymentBucket',
|
|
290
|
+
Effect: 'Allow',
|
|
291
|
+
Action: [
|
|
292
|
+
's3:CreateBucket',
|
|
293
|
+
's3:PutObject',
|
|
294
|
+
's3:GetObject',
|
|
295
|
+
's3:DeleteObject',
|
|
296
|
+
's3:PutBucketPolicy',
|
|
297
|
+
's3:PutBucketVersioning',
|
|
298
|
+
's3:PutBucketPublicAccessBlock',
|
|
299
|
+
's3:GetBucketLocation',
|
|
300
|
+
's3:ListBucket'
|
|
301
|
+
],
|
|
302
|
+
Resource: [
|
|
303
|
+
'arn:aws:s3:::*serverless*',
|
|
304
|
+
'arn:aws:s3:::*serverless*/*'
|
|
305
|
+
]
|
|
306
|
+
},
|
|
307
|
+
{
|
|
308
|
+
Sid: 'LambdaFriggFunctions',
|
|
309
|
+
Effect: 'Allow',
|
|
310
|
+
Action: [
|
|
311
|
+
'lambda:CreateFunction',
|
|
312
|
+
'lambda:UpdateFunctionCode',
|
|
313
|
+
'lambda:UpdateFunctionConfiguration',
|
|
314
|
+
'lambda:DeleteFunction',
|
|
315
|
+
'lambda:GetFunction',
|
|
316
|
+
'lambda:ListFunctions',
|
|
317
|
+
'lambda:PublishVersion',
|
|
318
|
+
'lambda:CreateAlias',
|
|
319
|
+
'lambda:UpdateAlias',
|
|
320
|
+
'lambda:DeleteAlias',
|
|
321
|
+
'lambda:GetAlias',
|
|
322
|
+
'lambda:AddPermission',
|
|
323
|
+
'lambda:RemovePermission',
|
|
324
|
+
'lambda:GetPolicy',
|
|
325
|
+
'lambda:PutProvisionedConcurrencyConfig',
|
|
326
|
+
'lambda:DeleteProvisionedConcurrencyConfig',
|
|
327
|
+
'lambda:PutConcurrency',
|
|
328
|
+
'lambda:DeleteConcurrency',
|
|
329
|
+
'lambda:TagResource',
|
|
330
|
+
'lambda:UntagResource',
|
|
331
|
+
'lambda:ListVersionsByFunction'
|
|
332
|
+
],
|
|
333
|
+
Resource: [
|
|
334
|
+
{ 'Fn::Sub': 'arn:aws:lambda:*:${AWS::AccountId}:function:*frigg*' }
|
|
335
|
+
]
|
|
336
|
+
},
|
|
337
|
+
{
|
|
338
|
+
Sid: 'IAMRolesForFriggLambda',
|
|
339
|
+
Effect: 'Allow',
|
|
340
|
+
Action: [
|
|
341
|
+
'iam:CreateRole',
|
|
342
|
+
'iam:DeleteRole',
|
|
343
|
+
'iam:GetRole',
|
|
344
|
+
'iam:PassRole',
|
|
345
|
+
'iam:PutRolePolicy',
|
|
346
|
+
'iam:DeleteRolePolicy',
|
|
347
|
+
'iam:GetRolePolicy',
|
|
348
|
+
'iam:AttachRolePolicy',
|
|
349
|
+
'iam:DetachRolePolicy',
|
|
350
|
+
'iam:TagRole',
|
|
351
|
+
'iam:UntagRole'
|
|
352
|
+
],
|
|
353
|
+
Resource: [
|
|
354
|
+
{ 'Fn::Sub': 'arn:aws:iam::${AWS::AccountId}:role/*frigg*' },
|
|
355
|
+
{ 'Fn::Sub': 'arn:aws:iam::${AWS::AccountId}:role/*frigg*LambdaRole*' }
|
|
356
|
+
]
|
|
357
|
+
},
|
|
358
|
+
{
|
|
359
|
+
Sid: 'IAMPolicyVersionPermissions',
|
|
360
|
+
Effect: 'Allow',
|
|
361
|
+
Action: ['iam:ListPolicyVersions'],
|
|
362
|
+
Resource: [{ 'Fn::Sub': 'arn:aws:iam::${AWS::AccountId}:policy/*' }]
|
|
363
|
+
},
|
|
364
|
+
{
|
|
365
|
+
Sid: 'FriggMessagingServices',
|
|
366
|
+
Effect: 'Allow',
|
|
367
|
+
Action: [
|
|
368
|
+
'sqs:CreateQueue',
|
|
369
|
+
'sqs:DeleteQueue',
|
|
370
|
+
'sqs:GetQueueAttributes',
|
|
371
|
+
'sqs:SetQueueAttributes',
|
|
372
|
+
'sqs:GetQueueUrl',
|
|
373
|
+
'sqs:TagQueue',
|
|
374
|
+
'sqs:UntagQueue'
|
|
375
|
+
],
|
|
376
|
+
Resource: [
|
|
377
|
+
{ 'Fn::Sub': 'arn:aws:sqs:*:${AWS::AccountId}:*frigg*' },
|
|
378
|
+
{ 'Fn::Sub': 'arn:aws:sqs:*:${AWS::AccountId}:internal-error-queue-*' }
|
|
379
|
+
]
|
|
380
|
+
},
|
|
381
|
+
{
|
|
382
|
+
Sid: 'FriggSNSTopics',
|
|
383
|
+
Effect: 'Allow',
|
|
384
|
+
Action: [
|
|
385
|
+
'sns:CreateTopic',
|
|
386
|
+
'sns:DeleteTopic',
|
|
387
|
+
'sns:GetTopicAttributes',
|
|
388
|
+
'sns:SetTopicAttributes',
|
|
389
|
+
'sns:Subscribe',
|
|
390
|
+
'sns:Unsubscribe',
|
|
391
|
+
'sns:ListSubscriptionsByTopic',
|
|
392
|
+
'sns:TagResource',
|
|
393
|
+
'sns:UntagResource'
|
|
394
|
+
],
|
|
395
|
+
Resource: [
|
|
396
|
+
{ 'Fn::Sub': 'arn:aws:sns:*:${AWS::AccountId}:*frigg*' }
|
|
397
|
+
]
|
|
398
|
+
},
|
|
399
|
+
{
|
|
400
|
+
Sid: 'FriggMonitoringAndLogs',
|
|
401
|
+
Effect: 'Allow',
|
|
402
|
+
Action: [
|
|
403
|
+
'cloudwatch:PutMetricAlarm',
|
|
404
|
+
'cloudwatch:DeleteAlarms',
|
|
405
|
+
'cloudwatch:DescribeAlarms',
|
|
406
|
+
'logs:CreateLogGroup',
|
|
407
|
+
'logs:CreateLogStream',
|
|
408
|
+
'logs:DeleteLogGroup',
|
|
409
|
+
'logs:DescribeLogGroups',
|
|
410
|
+
'logs:DescribeLogStreams',
|
|
411
|
+
'logs:FilterLogEvents',
|
|
412
|
+
'logs:PutLogEvents',
|
|
413
|
+
'logs:PutRetentionPolicy'
|
|
414
|
+
],
|
|
415
|
+
Resource: [
|
|
416
|
+
{ 'Fn::Sub': 'arn:aws:logs:*:${AWS::AccountId}:log-group:/aws/lambda/*frigg*' },
|
|
417
|
+
{ 'Fn::Sub': 'arn:aws:logs:*:${AWS::AccountId}:log-group:/aws/lambda/*frigg*:*' },
|
|
418
|
+
{ 'Fn::Sub': 'arn:aws:cloudwatch:*:${AWS::AccountId}:alarm:*frigg*' }
|
|
419
|
+
]
|
|
420
|
+
},
|
|
421
|
+
{
|
|
422
|
+
Sid: 'FriggAPIGateway',
|
|
423
|
+
Effect: 'Allow',
|
|
424
|
+
Action: [
|
|
425
|
+
'apigateway:POST',
|
|
426
|
+
'apigateway:PUT',
|
|
427
|
+
'apigateway:DELETE',
|
|
428
|
+
'apigateway:GET',
|
|
429
|
+
'apigateway:PATCH'
|
|
430
|
+
],
|
|
431
|
+
Resource: [
|
|
432
|
+
'arn:aws:apigateway:*::/restapis',
|
|
433
|
+
'arn:aws:apigateway:*::/restapis/*',
|
|
434
|
+
'arn:aws:apigateway:*::/domainnames',
|
|
435
|
+
'arn:aws:apigateway:*::/domainnames/*'
|
|
436
|
+
]
|
|
437
|
+
}
|
|
438
|
+
];
|
|
439
|
+
|
|
440
|
+
template.Resources.FriggCoreDeploymentPolicy = {
|
|
441
|
+
Type: 'AWS::IAM::ManagedPolicy',
|
|
442
|
+
Properties: {
|
|
443
|
+
ManagedPolicyName: 'FriggCoreDeploymentPolicy',
|
|
444
|
+
Description: 'Core permissions for deploying Frigg applications',
|
|
445
|
+
PolicyDocument: {
|
|
446
|
+
Version: '2012-10-17',
|
|
447
|
+
Statement: coreStatements
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
};
|
|
451
|
+
|
|
452
|
+
// Add feature-specific policies only if needed
|
|
453
|
+
if (features.vpc) {
|
|
454
|
+
template.Resources.FriggVPCPolicy = {
|
|
455
|
+
Type: 'AWS::IAM::ManagedPolicy',
|
|
456
|
+
Condition: 'CreateVPCPermissions',
|
|
457
|
+
Properties: {
|
|
458
|
+
ManagedPolicyName: 'FriggVPCPolicy',
|
|
459
|
+
Description: 'VPC-related permissions for Frigg applications',
|
|
460
|
+
PolicyDocument: {
|
|
461
|
+
Version: '2012-10-17',
|
|
462
|
+
Statement: [
|
|
463
|
+
{
|
|
464
|
+
Sid: 'FriggVPCEndpointManagement',
|
|
465
|
+
Effect: 'Allow',
|
|
466
|
+
Action: [
|
|
467
|
+
'ec2:CreateVpcEndpoint',
|
|
468
|
+
'ec2:DeleteVpcEndpoint',
|
|
469
|
+
'ec2:DescribeVpcEndpoints',
|
|
470
|
+
'ec2:ModifyVpcEndpoint',
|
|
471
|
+
'ec2:CreateNatGateway',
|
|
472
|
+
'ec2:DeleteNatGateway',
|
|
473
|
+
'ec2:DescribeNatGateways',
|
|
474
|
+
'ec2:AllocateAddress',
|
|
475
|
+
'ec2:ReleaseAddress',
|
|
476
|
+
'ec2:DescribeAddresses',
|
|
477
|
+
'ec2:CreateRouteTable',
|
|
478
|
+
'ec2:DeleteRouteTable',
|
|
479
|
+
'ec2:DescribeRouteTables',
|
|
480
|
+
'ec2:CreateRoute',
|
|
481
|
+
'ec2:DeleteRoute',
|
|
482
|
+
'ec2:AssociateRouteTable',
|
|
483
|
+
'ec2:DisassociateRouteTable',
|
|
484
|
+
'ec2:CreateSecurityGroup',
|
|
485
|
+
'ec2:DeleteSecurityGroup',
|
|
486
|
+
'ec2:AuthorizeSecurityGroupEgress',
|
|
487
|
+
'ec2:AuthorizeSecurityGroupIngress',
|
|
488
|
+
'ec2:RevokeSecurityGroupEgress',
|
|
489
|
+
'ec2:RevokeSecurityGroupIngress'
|
|
490
|
+
],
|
|
491
|
+
Resource: '*'
|
|
492
|
+
}
|
|
493
|
+
]
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
};
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
if (features.kms) {
|
|
500
|
+
template.Resources.FriggKMSPolicy = {
|
|
501
|
+
Type: 'AWS::IAM::ManagedPolicy',
|
|
502
|
+
Condition: 'CreateKMSPermissions',
|
|
503
|
+
Properties: {
|
|
504
|
+
ManagedPolicyName: 'FriggKMSPolicy',
|
|
505
|
+
Description: 'KMS encryption permissions for Frigg applications',
|
|
506
|
+
PolicyDocument: {
|
|
507
|
+
Version: '2012-10-17',
|
|
508
|
+
Statement: [
|
|
509
|
+
{
|
|
510
|
+
Sid: 'FriggKMSEncryptionRuntime',
|
|
511
|
+
Effect: 'Allow',
|
|
512
|
+
Action: [
|
|
513
|
+
'kms:GenerateDataKey',
|
|
514
|
+
'kms:Decrypt'
|
|
515
|
+
],
|
|
516
|
+
Resource: [
|
|
517
|
+
{ 'Fn::Sub': 'arn:aws:kms:*:${AWS::AccountId}:key/*' }
|
|
518
|
+
],
|
|
519
|
+
Condition: {
|
|
520
|
+
StringEquals: {
|
|
521
|
+
'kms:ViaService': [
|
|
522
|
+
'lambda.*.amazonaws.com',
|
|
523
|
+
's3.*.amazonaws.com'
|
|
524
|
+
]
|
|
525
|
+
}
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
]
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
};
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
if (features.ssm) {
|
|
535
|
+
template.Resources.FriggSSMPolicy = {
|
|
536
|
+
Type: 'AWS::IAM::ManagedPolicy',
|
|
537
|
+
Condition: 'CreateSSMPermissions',
|
|
538
|
+
Properties: {
|
|
539
|
+
ManagedPolicyName: 'FriggSSMPolicy',
|
|
540
|
+
Description: 'SSM Parameter Store permissions for Frigg applications',
|
|
541
|
+
PolicyDocument: {
|
|
542
|
+
Version: '2012-10-17',
|
|
543
|
+
Statement: [
|
|
544
|
+
{
|
|
545
|
+
Sid: 'FriggSSMParameterAccess',
|
|
546
|
+
Effect: 'Allow',
|
|
547
|
+
Action: [
|
|
548
|
+
'ssm:GetParameter',
|
|
549
|
+
'ssm:GetParameters',
|
|
550
|
+
'ssm:GetParametersByPath'
|
|
551
|
+
],
|
|
552
|
+
Resource: [
|
|
553
|
+
{ 'Fn::Sub': 'arn:aws:ssm:*:${AWS::AccountId}:parameter/*frigg*' },
|
|
554
|
+
{ 'Fn::Sub': 'arn:aws:ssm:*:${AWS::AccountId}:parameter/*frigg*/*' }
|
|
555
|
+
]
|
|
556
|
+
}
|
|
557
|
+
]
|
|
558
|
+
}
|
|
559
|
+
}
|
|
560
|
+
};
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
// Add Secrets Manager for credentials
|
|
564
|
+
template.Resources.FriggDeploymentCredentials = {
|
|
565
|
+
Type: 'AWS::SecretsManager::Secret',
|
|
566
|
+
Properties: {
|
|
567
|
+
Name: 'frigg-deployment-credentials',
|
|
568
|
+
Description: 'Access credentials for Frigg deployment user',
|
|
569
|
+
SecretString: {
|
|
570
|
+
'Fn::Sub': JSON.stringify({
|
|
571
|
+
AccessKeyId: '${FriggDeploymentAccessKey}',
|
|
572
|
+
SecretAccessKey: '${FriggDeploymentAccessKey.SecretAccessKey}'
|
|
573
|
+
})
|
|
574
|
+
}
|
|
575
|
+
}
|
|
576
|
+
};
|
|
577
|
+
|
|
578
|
+
// Add Outputs
|
|
579
|
+
template.Outputs = {
|
|
580
|
+
DeploymentUserArn: {
|
|
581
|
+
Description: 'ARN of the Frigg deployment user',
|
|
582
|
+
Value: { 'Fn::GetAtt': ['FriggDeploymentUser', 'Arn'] },
|
|
583
|
+
Export: {
|
|
584
|
+
Name: { 'Fn::Sub': '${AWS::StackName}-UserArn' }
|
|
585
|
+
}
|
|
586
|
+
},
|
|
587
|
+
AccessKeyId: {
|
|
588
|
+
Description: 'Access Key ID for the deployment user',
|
|
589
|
+
Value: { Ref: 'FriggDeploymentAccessKey' },
|
|
590
|
+
Export: {
|
|
591
|
+
Name: { 'Fn::Sub': '${AWS::StackName}-AccessKeyId' }
|
|
592
|
+
}
|
|
593
|
+
},
|
|
594
|
+
SecretAccessKeyCommand: {
|
|
595
|
+
Description: 'Command to retrieve the secret access key',
|
|
596
|
+
Value: {
|
|
597
|
+
'Fn::Sub': 'aws secretsmanager get-secret-value --secret-id frigg-deployment-credentials --query SecretString --output text | jq -r .SecretAccessKey'
|
|
598
|
+
}
|
|
599
|
+
},
|
|
600
|
+
CredentialsSecretArn: {
|
|
601
|
+
Description: 'ARN of the secret containing deployment credentials',
|
|
602
|
+
Value: { Ref: 'FriggDeploymentCredentials' },
|
|
603
|
+
Export: {
|
|
604
|
+
Name: { 'Fn::Sub': '${AWS::StackName}-CredentialsSecretArn' }
|
|
605
|
+
}
|
|
606
|
+
}
|
|
607
|
+
};
|
|
608
|
+
|
|
609
|
+
// Convert to YAML
|
|
610
|
+
return convertToYAML(template);
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
/**
|
|
614
|
+
* Convert JavaScript object to CloudFormation YAML
|
|
615
|
+
* @param {Object} obj - JavaScript object
|
|
616
|
+
* @returns {string} YAML string
|
|
617
|
+
*/
|
|
618
|
+
function convertToYAML(obj) {
|
|
619
|
+
const yaml = require('js-yaml');
|
|
620
|
+
return yaml.dump(obj, {
|
|
621
|
+
indent: 2,
|
|
622
|
+
lineWidth: 120,
|
|
623
|
+
noRefs: true,
|
|
624
|
+
sortKeys: false
|
|
625
|
+
});
|
|
626
|
+
}
|
|
627
|
+
|
|
628
|
+
/**
|
|
629
|
+
* Generate summary of what features will be included in the IAM policy
|
|
630
|
+
* @param {Object} appDefinition - Application definition
|
|
631
|
+
* @returns {Object} Feature summary
|
|
632
|
+
*/
|
|
633
|
+
function getFeatureSummary(appDefinition) {
|
|
634
|
+
const features = {
|
|
635
|
+
core: true, // Always enabled
|
|
636
|
+
vpc: appDefinition.vpc?.enable === true,
|
|
637
|
+
kms: appDefinition.encryption?.useDefaultKMSForFieldLevelEncryption === true,
|
|
638
|
+
ssm: appDefinition.ssm?.enable === true,
|
|
639
|
+
websockets: appDefinition.websockets?.enable === true
|
|
640
|
+
};
|
|
641
|
+
|
|
642
|
+
const integrationCount = appDefinition.integrations?.length || 0;
|
|
643
|
+
|
|
644
|
+
return {
|
|
645
|
+
features,
|
|
646
|
+
integrationCount,
|
|
647
|
+
appName: appDefinition.name || 'Unnamed Frigg App'
|
|
648
|
+
};
|
|
649
|
+
}
|
|
650
|
+
|
|
651
|
+
/**
|
|
652
|
+
* Generate basic IAM policy (JSON format) - Core Frigg permissions only
|
|
653
|
+
* @returns {Object} Basic IAM policy document
|
|
654
|
+
*/
|
|
655
|
+
function generateBasicIAMPolicy() {
|
|
656
|
+
const basicPolicyPath = path.join(__dirname, 'iam-policy-basic.json');
|
|
657
|
+
return require(basicPolicyPath);
|
|
658
|
+
}
|
|
659
|
+
|
|
660
|
+
/**
|
|
661
|
+
* Generate full IAM policy (JSON format) - All features enabled
|
|
662
|
+
* @returns {Object} Full IAM policy document
|
|
663
|
+
*/
|
|
664
|
+
function generateFullIAMPolicy() {
|
|
665
|
+
const fullPolicyPath = path.join(__dirname, 'iam-policy-full.json');
|
|
666
|
+
return require(fullPolicyPath);
|
|
667
|
+
}
|
|
668
|
+
|
|
669
|
+
/**
|
|
670
|
+
* Generate IAM policy based on mode
|
|
671
|
+
* @param {string} mode - 'basic' or 'full'
|
|
672
|
+
* @returns {Object} IAM policy document
|
|
673
|
+
*/
|
|
674
|
+
function generateIAMPolicy(mode = 'basic') {
|
|
675
|
+
if (mode === 'full') {
|
|
676
|
+
return generateFullIAMPolicy();
|
|
677
|
+
}
|
|
678
|
+
return generateBasicIAMPolicy();
|
|
679
|
+
}
|
|
680
|
+
|
|
681
|
+
module.exports = {
|
|
682
|
+
generateIAMCloudFormation,
|
|
683
|
+
getFeatureSummary,
|
|
684
|
+
generateBasicIAMPolicy,
|
|
685
|
+
generateFullIAMPolicy,
|
|
686
|
+
generateIAMPolicy
|
|
687
|
+
};
|