@friggframework/devtools 2.0.0-next.62 → 2.0.0-next.63

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.
Files changed (165) hide show
  1. package/infrastructure/ARCHITECTURE.md +487 -0
  2. package/infrastructure/CLAUDE.md +481 -0
  3. package/infrastructure/HEALTH.md +468 -0
  4. package/infrastructure/README.md +522 -0
  5. package/infrastructure/__tests__/fixtures/mock-aws-resources.js +391 -0
  6. package/infrastructure/__tests__/helpers/test-utils.js +277 -0
  7. package/infrastructure/__tests__/postgres-config.test.js +914 -0
  8. package/infrastructure/__tests__/template-generation.test.js +687 -0
  9. package/infrastructure/create-frigg-infrastructure.js +147 -0
  10. package/infrastructure/docs/POSTGRES-CONFIGURATION.md +630 -0
  11. package/infrastructure/docs/PRE-DEPLOYMENT-HEALTH-CHECK-SPEC.md +1317 -0
  12. package/infrastructure/docs/WEBSOCKET-CONFIGURATION.md +105 -0
  13. package/infrastructure/docs/deployment-instructions.md +268 -0
  14. package/infrastructure/docs/generate-iam-command.md +278 -0
  15. package/infrastructure/docs/iam-policy-templates.md +193 -0
  16. package/infrastructure/domains/database/aurora-builder.js +809 -0
  17. package/infrastructure/domains/database/aurora-builder.test.js +950 -0
  18. package/infrastructure/domains/database/aurora-discovery.js +87 -0
  19. package/infrastructure/domains/database/aurora-discovery.test.js +188 -0
  20. package/infrastructure/domains/database/aurora-resolver.js +210 -0
  21. package/infrastructure/domains/database/aurora-resolver.test.js +347 -0
  22. package/infrastructure/domains/database/migration-builder.js +701 -0
  23. package/infrastructure/domains/database/migration-builder.test.js +321 -0
  24. package/infrastructure/domains/database/migration-resolver.js +163 -0
  25. package/infrastructure/domains/database/migration-resolver.test.js +337 -0
  26. package/infrastructure/domains/health/application/ports/IPropertyReconciler.js +164 -0
  27. package/infrastructure/domains/health/application/ports/IResourceDetector.js +129 -0
  28. package/infrastructure/domains/health/application/ports/IResourceImporter.js +142 -0
  29. package/infrastructure/domains/health/application/ports/IStackRepository.js +131 -0
  30. package/infrastructure/domains/health/application/ports/index.js +26 -0
  31. package/infrastructure/domains/health/application/use-cases/__tests__/execute-resource-import-use-case.test.js +679 -0
  32. package/infrastructure/domains/health/application/use-cases/__tests__/mismatch-analyzer-method-name.test.js +167 -0
  33. package/infrastructure/domains/health/application/use-cases/__tests__/repair-via-import-use-case.test.js +1130 -0
  34. package/infrastructure/domains/health/application/use-cases/execute-resource-import-use-case.js +221 -0
  35. package/infrastructure/domains/health/application/use-cases/reconcile-properties-use-case.js +152 -0
  36. package/infrastructure/domains/health/application/use-cases/reconcile-properties-use-case.test.js +343 -0
  37. package/infrastructure/domains/health/application/use-cases/repair-via-import-use-case.js +535 -0
  38. package/infrastructure/domains/health/application/use-cases/repair-via-import-use-case.test.js +376 -0
  39. package/infrastructure/domains/health/application/use-cases/run-health-check-use-case.js +213 -0
  40. package/infrastructure/domains/health/application/use-cases/run-health-check-use-case.test.js +441 -0
  41. package/infrastructure/domains/health/docs/ACME-DEV-DRIFT-ANALYSIS.md +267 -0
  42. package/infrastructure/domains/health/docs/BUILD-VS-DEPLOYED-TEMPLATE-ANALYSIS.md +324 -0
  43. package/infrastructure/domains/health/docs/ORPHAN-DETECTION-ANALYSIS.md +386 -0
  44. package/infrastructure/domains/health/docs/SPEC-CLEANUP-COMMAND.md +1419 -0
  45. package/infrastructure/domains/health/docs/TDD-IMPLEMENTATION-SUMMARY.md +391 -0
  46. package/infrastructure/domains/health/docs/TEMPLATE-COMPARISON-IMPLEMENTATION.md +551 -0
  47. package/infrastructure/domains/health/domain/entities/issue.js +299 -0
  48. package/infrastructure/domains/health/domain/entities/issue.test.js +528 -0
  49. package/infrastructure/domains/health/domain/entities/property-mismatch.js +108 -0
  50. package/infrastructure/domains/health/domain/entities/property-mismatch.test.js +275 -0
  51. package/infrastructure/domains/health/domain/entities/resource.js +159 -0
  52. package/infrastructure/domains/health/domain/entities/resource.test.js +432 -0
  53. package/infrastructure/domains/health/domain/entities/stack-health-report.js +306 -0
  54. package/infrastructure/domains/health/domain/entities/stack-health-report.test.js +601 -0
  55. package/infrastructure/domains/health/domain/services/__tests__/health-score-percentage-based.test.js +380 -0
  56. package/infrastructure/domains/health/domain/services/__tests__/import-progress-monitor.test.js +971 -0
  57. package/infrastructure/domains/health/domain/services/__tests__/import-template-generator.test.js +1150 -0
  58. package/infrastructure/domains/health/domain/services/__tests__/logical-id-mapper.test.js +672 -0
  59. package/infrastructure/domains/health/domain/services/__tests__/template-parser.test.js +496 -0
  60. package/infrastructure/domains/health/domain/services/__tests__/update-progress-monitor.test.js +419 -0
  61. package/infrastructure/domains/health/domain/services/health-score-calculator.js +248 -0
  62. package/infrastructure/domains/health/domain/services/health-score-calculator.test.js +504 -0
  63. package/infrastructure/domains/health/domain/services/import-progress-monitor.js +195 -0
  64. package/infrastructure/domains/health/domain/services/import-template-generator.js +435 -0
  65. package/infrastructure/domains/health/domain/services/logical-id-mapper.js +345 -0
  66. package/infrastructure/domains/health/domain/services/mismatch-analyzer.js +234 -0
  67. package/infrastructure/domains/health/domain/services/mismatch-analyzer.test.js +431 -0
  68. package/infrastructure/domains/health/domain/services/property-mutability-config.js +382 -0
  69. package/infrastructure/domains/health/domain/services/template-parser.js +245 -0
  70. package/infrastructure/domains/health/domain/services/update-progress-monitor.js +192 -0
  71. package/infrastructure/domains/health/domain/value-objects/health-score.js +138 -0
  72. package/infrastructure/domains/health/domain/value-objects/health-score.test.js +267 -0
  73. package/infrastructure/domains/health/domain/value-objects/property-mutability.js +161 -0
  74. package/infrastructure/domains/health/domain/value-objects/property-mutability.test.js +198 -0
  75. package/infrastructure/domains/health/domain/value-objects/resource-state.js +167 -0
  76. package/infrastructure/domains/health/domain/value-objects/resource-state.test.js +196 -0
  77. package/infrastructure/domains/health/domain/value-objects/stack-identifier.js +192 -0
  78. package/infrastructure/domains/health/domain/value-objects/stack-identifier.test.js +262 -0
  79. package/infrastructure/domains/health/infrastructure/adapters/__tests__/orphan-detection-cfn-tagged.test.js +312 -0
  80. package/infrastructure/domains/health/infrastructure/adapters/__tests__/orphan-detection-multi-stack.test.js +367 -0
  81. package/infrastructure/domains/health/infrastructure/adapters/__tests__/orphan-detection-relationship-analysis.test.js +432 -0
  82. package/infrastructure/domains/health/infrastructure/adapters/aws-property-reconciler.js +784 -0
  83. package/infrastructure/domains/health/infrastructure/adapters/aws-property-reconciler.test.js +1133 -0
  84. package/infrastructure/domains/health/infrastructure/adapters/aws-resource-detector.js +565 -0
  85. package/infrastructure/domains/health/infrastructure/adapters/aws-resource-detector.test.js +554 -0
  86. package/infrastructure/domains/health/infrastructure/adapters/aws-resource-importer.js +318 -0
  87. package/infrastructure/domains/health/infrastructure/adapters/aws-resource-importer.test.js +398 -0
  88. package/infrastructure/domains/health/infrastructure/adapters/aws-stack-repository.js +777 -0
  89. package/infrastructure/domains/health/infrastructure/adapters/aws-stack-repository.test.js +580 -0
  90. package/infrastructure/domains/integration/integration-builder.js +404 -0
  91. package/infrastructure/domains/integration/integration-builder.test.js +690 -0
  92. package/infrastructure/domains/integration/integration-resolver.js +170 -0
  93. package/infrastructure/domains/integration/integration-resolver.test.js +369 -0
  94. package/infrastructure/domains/integration/websocket-builder.js +69 -0
  95. package/infrastructure/domains/integration/websocket-builder.test.js +195 -0
  96. package/infrastructure/domains/networking/vpc-builder.js +2051 -0
  97. package/infrastructure/domains/networking/vpc-builder.test.js +1960 -0
  98. package/infrastructure/domains/networking/vpc-discovery.js +177 -0
  99. package/infrastructure/domains/networking/vpc-discovery.test.js +350 -0
  100. package/infrastructure/domains/networking/vpc-resolver.js +505 -0
  101. package/infrastructure/domains/networking/vpc-resolver.test.js +801 -0
  102. package/infrastructure/domains/parameters/ssm-builder.js +79 -0
  103. package/infrastructure/domains/parameters/ssm-builder.test.js +189 -0
  104. package/infrastructure/domains/parameters/ssm-discovery.js +84 -0
  105. package/infrastructure/domains/parameters/ssm-discovery.test.js +210 -0
  106. package/infrastructure/domains/security/iam-generator.js +816 -0
  107. package/infrastructure/domains/security/iam-generator.test.js +204 -0
  108. package/infrastructure/domains/security/kms-builder.js +415 -0
  109. package/infrastructure/domains/security/kms-builder.test.js +392 -0
  110. package/infrastructure/domains/security/kms-discovery.js +80 -0
  111. package/infrastructure/domains/security/kms-discovery.test.js +177 -0
  112. package/infrastructure/domains/security/kms-resolver.js +96 -0
  113. package/infrastructure/domains/security/kms-resolver.test.js +216 -0
  114. package/infrastructure/domains/security/templates/frigg-deployment-iam-stack.yaml +401 -0
  115. package/infrastructure/domains/security/templates/iam-policy-basic.json +218 -0
  116. package/infrastructure/domains/security/templates/iam-policy-full.json +288 -0
  117. package/infrastructure/domains/shared/base-builder.js +112 -0
  118. package/infrastructure/domains/shared/base-resolver.js +186 -0
  119. package/infrastructure/domains/shared/base-resolver.test.js +305 -0
  120. package/infrastructure/domains/shared/builder-orchestrator.js +212 -0
  121. package/infrastructure/domains/shared/builder-orchestrator.test.js +213 -0
  122. package/infrastructure/domains/shared/cloudformation-discovery-v2.js +334 -0
  123. package/infrastructure/domains/shared/cloudformation-discovery.js +672 -0
  124. package/infrastructure/domains/shared/cloudformation-discovery.test.js +985 -0
  125. package/infrastructure/domains/shared/environment-builder.js +119 -0
  126. package/infrastructure/domains/shared/environment-builder.test.js +247 -0
  127. package/infrastructure/domains/shared/providers/aws-provider-adapter.js +579 -0
  128. package/infrastructure/domains/shared/providers/aws-provider-adapter.test.js +416 -0
  129. package/infrastructure/domains/shared/providers/azure-provider-adapter.stub.js +93 -0
  130. package/infrastructure/domains/shared/providers/cloud-provider-adapter.js +136 -0
  131. package/infrastructure/domains/shared/providers/gcp-provider-adapter.stub.js +82 -0
  132. package/infrastructure/domains/shared/providers/provider-factory.js +108 -0
  133. package/infrastructure/domains/shared/providers/provider-factory.test.js +170 -0
  134. package/infrastructure/domains/shared/resource-discovery.enhanced.test.js +306 -0
  135. package/infrastructure/domains/shared/resource-discovery.js +233 -0
  136. package/infrastructure/domains/shared/resource-discovery.test.js +588 -0
  137. package/infrastructure/domains/shared/types/app-definition.js +205 -0
  138. package/infrastructure/domains/shared/types/discovery-result.js +106 -0
  139. package/infrastructure/domains/shared/types/discovery-result.test.js +258 -0
  140. package/infrastructure/domains/shared/types/index.js +46 -0
  141. package/infrastructure/domains/shared/types/resource-ownership.js +108 -0
  142. package/infrastructure/domains/shared/types/resource-ownership.test.js +101 -0
  143. package/infrastructure/domains/shared/utilities/base-definition-factory.js +408 -0
  144. package/infrastructure/domains/shared/utilities/base-definition-factory.js.bak +338 -0
  145. package/infrastructure/domains/shared/utilities/base-definition-factory.test.js +291 -0
  146. package/infrastructure/domains/shared/utilities/handler-path-resolver.js +134 -0
  147. package/infrastructure/domains/shared/utilities/handler-path-resolver.test.js +268 -0
  148. package/infrastructure/domains/shared/utilities/prisma-layer-manager.js +159 -0
  149. package/infrastructure/domains/shared/utilities/prisma-layer-manager.test.js +444 -0
  150. package/infrastructure/domains/shared/validation/env-validator.js +78 -0
  151. package/infrastructure/domains/shared/validation/env-validator.test.js +173 -0
  152. package/infrastructure/domains/shared/validation/plugin-validator.js +187 -0
  153. package/infrastructure/domains/shared/validation/plugin-validator.test.js +323 -0
  154. package/infrastructure/esbuild.config.js +53 -0
  155. package/infrastructure/index.js +4 -0
  156. package/infrastructure/infrastructure-composer.js +117 -0
  157. package/infrastructure/infrastructure-composer.test.js +1895 -0
  158. package/infrastructure/integration.test.js +383 -0
  159. package/infrastructure/scripts/build-prisma-layer.js +701 -0
  160. package/infrastructure/scripts/build-prisma-layer.test.js +170 -0
  161. package/infrastructure/scripts/build-time-discovery.js +238 -0
  162. package/infrastructure/scripts/build-time-discovery.test.js +379 -0
  163. package/infrastructure/scripts/run-discovery.js +110 -0
  164. package/infrastructure/scripts/verify-prisma-layer.js +72 -0
  165. package/package.json +8 -7
@@ -0,0 +1,816 @@
1
+ const path = require('path');
2
+
3
+ /**
4
+ * Generate IAM CloudFormation template
5
+ * @param {Object} options - Generation options
6
+ * @param {string} [options.appName='Frigg'] - Application name
7
+ * @param {Object} [options.features={}] - Enabled features { vpc, kms, ssm, websockets }
8
+ * @param {string} [options.userPrefix='frigg-deployment-user'] - IAM user name prefix
9
+ * @param {string} [options.stackName='frigg-deployment-iam'] - CloudFormation stack name
10
+ * @returns {string} CloudFormation YAML template
11
+ */
12
+ function generateIAMCloudFormation(options = {}) {
13
+ const {
14
+ appName = 'Frigg',
15
+ features = {},
16
+ userPrefix = 'frigg-deployment-user',
17
+ stackName = 'frigg-deployment-iam'
18
+ } = options;
19
+
20
+ const deploymentUserName = userPrefix;
21
+
22
+ // Features are already analyzed by caller (use getFeatureSummary to extract features from appDefinition)
23
+ // Expected features: { vpc, kms, ssm, websockets }
24
+
25
+ // Build the CloudFormation template
26
+ const template = {
27
+ AWSTemplateFormatVersion: '2010-09-09',
28
+ Description: `IAM roles and policies for ${appName} application deployment pipeline`,
29
+
30
+ Parameters: {
31
+ DeploymentUserName: {
32
+ Type: 'String',
33
+ Default: deploymentUserName,
34
+ Description:
35
+ 'Name for the IAM user that will deploy Frigg applications',
36
+ },
37
+ EnableVPCSupport: {
38
+ Type: 'String',
39
+ Default: features.vpc ? 'true' : 'false',
40
+ AllowedValues: ['true', 'false'],
41
+ Description:
42
+ 'Enable VPC-related permissions for Frigg applications',
43
+ },
44
+ EnableKMSSupport: {
45
+ Type: 'String',
46
+ Default: features.kms ? 'true' : 'false',
47
+ AllowedValues: ['true', 'false'],
48
+ Description:
49
+ 'Enable KMS encryption permissions for Frigg applications',
50
+ },
51
+ EnableSSMSupport: {
52
+ Type: 'String',
53
+ Default: features.ssm ? 'true' : 'false',
54
+ AllowedValues: ['true', 'false'],
55
+ Description:
56
+ 'Enable SSM Parameter Store permissions for Frigg applications',
57
+ },
58
+ DeploymentKmsAliasName: {
59
+ Type: 'String',
60
+ Default: 'alias/frigg-deployment',
61
+ Description:
62
+ 'Alias name to create or manage for the deployment KMS key',
63
+ },
64
+ DeploymentKmsTargetKeyArn: {
65
+ Type: 'String',
66
+ Default: '',
67
+ Description:
68
+ 'Optional existing KMS key ARN that the deployment alias should reference',
69
+ },
70
+ },
71
+
72
+ Conditions: {
73
+ CreateVPCPermissions: {
74
+ 'Fn::Equals': [{ Ref: 'EnableVPCSupport' }, 'true'],
75
+ },
76
+ CreateKMSPermissions: {
77
+ 'Fn::Equals': [{ Ref: 'EnableKMSSupport' }, 'true'],
78
+ },
79
+ CreateSSMPermissions: {
80
+ 'Fn::Equals': [{ Ref: 'EnableSSMSupport' }, 'true'],
81
+ },
82
+ CreateKMSAlias: {
83
+ 'Fn::And': [
84
+ {
85
+ 'Fn::Equals': [{ Ref: 'EnableKMSSupport' }, 'true'],
86
+ },
87
+ {
88
+ 'Fn::Not': [
89
+ {
90
+ 'Fn::Equals': [
91
+ { Ref: 'DeploymentKmsTargetKeyArn' },
92
+ '',
93
+ ],
94
+ },
95
+ ],
96
+ },
97
+ ],
98
+ },
99
+ },
100
+
101
+ Resources: {},
102
+ };
103
+
104
+ // Add IAM User
105
+ template.Resources.FriggDeploymentUser = {
106
+ Type: 'AWS::IAM::User',
107
+ Properties: {
108
+ UserName: { Ref: 'DeploymentUserName' },
109
+ ManagedPolicyArns: [
110
+ { Ref: 'FriggDiscoveryPolicy' },
111
+ { Ref: 'FriggCoreDeploymentPolicy' },
112
+ ],
113
+ },
114
+ };
115
+
116
+ // Conditionally add feature-specific policies
117
+ if (features.vpc) {
118
+ template.Resources.FriggDeploymentUser.Properties.ManagedPolicyArns.push(
119
+ {
120
+ 'Fn::If': [
121
+ 'CreateVPCPermissions',
122
+ { Ref: 'FriggVPCPolicy' },
123
+ { Ref: 'AWS::NoValue' },
124
+ ],
125
+ }
126
+ );
127
+ }
128
+ if (features.kms) {
129
+ template.Resources.FriggDeploymentUser.Properties.ManagedPolicyArns.push(
130
+ {
131
+ 'Fn::If': [
132
+ 'CreateKMSPermissions',
133
+ { Ref: 'FriggKMSPolicy' },
134
+ { Ref: 'AWS::NoValue' },
135
+ ],
136
+ }
137
+ );
138
+ }
139
+ if (features.ssm) {
140
+ template.Resources.FriggDeploymentUser.Properties.ManagedPolicyArns.push(
141
+ {
142
+ 'Fn::If': [
143
+ 'CreateSSMPermissions',
144
+ { Ref: 'FriggSSMPolicy' },
145
+ { Ref: 'AWS::NoValue' },
146
+ ],
147
+ }
148
+ );
149
+ }
150
+
151
+ // Add Access Key
152
+ template.Resources.FriggDeploymentAccessKey = {
153
+ Type: 'AWS::IAM::AccessKey',
154
+ Properties: {
155
+ UserName: { Ref: 'FriggDeploymentUser' },
156
+ },
157
+ };
158
+
159
+ // Add Discovery Policy (always needed)
160
+ template.Resources.FriggDiscoveryPolicy = {
161
+ Type: 'AWS::IAM::ManagedPolicy',
162
+ Properties: {
163
+ ManagedPolicyName: 'FriggDiscoveryPolicy',
164
+ Description:
165
+ 'Permissions for AWS resource discovery during Frigg build process',
166
+ PolicyDocument: {
167
+ Version: '2012-10-17',
168
+ Statement: [
169
+ {
170
+ Sid: 'AWSDiscoveryPermissions',
171
+ Effect: 'Allow',
172
+ Action: [
173
+ 'sts:GetCallerIdentity',
174
+ 'ec2:DescribeVpcs',
175
+ 'ec2:DescribeSubnets',
176
+ 'ec2:DescribeSecurityGroups',
177
+ 'ec2:DescribeRouteTables',
178
+ 'ec2:DescribeNatGateways',
179
+ 'ec2:DescribeAddresses',
180
+ 'kms:ListKeys',
181
+ 'kms:DescribeKey',
182
+ ],
183
+ Resource: '*',
184
+ },
185
+ ],
186
+ },
187
+ },
188
+ };
189
+
190
+ // Add Core Deployment Policy (always needed)
191
+ const coreActions = [
192
+ // CloudFormation permissions
193
+ 'cloudformation:CreateStack',
194
+ 'cloudformation:UpdateStack',
195
+ 'cloudformation:DeleteStack',
196
+ 'cloudformation:DescribeStacks',
197
+ 'cloudformation:DescribeStackEvents',
198
+ 'cloudformation:DescribeStackResources',
199
+ 'cloudformation:DescribeStackResource',
200
+ 'cloudformation:ListStackResources',
201
+ 'cloudformation:GetTemplate',
202
+ 'cloudformation:DescribeChangeSet',
203
+ 'cloudformation:CreateChangeSet',
204
+ 'cloudformation:DeleteChangeSet',
205
+ 'cloudformation:ExecuteChangeSet',
206
+ 'cloudformation:ValidateTemplate',
207
+
208
+ // Lambda permissions
209
+ 'lambda:CreateFunction',
210
+ 'lambda:UpdateFunctionCode',
211
+ 'lambda:UpdateFunctionConfiguration',
212
+ 'lambda:DeleteFunction',
213
+ 'lambda:GetFunction',
214
+ 'lambda:ListFunctions',
215
+ 'lambda:PublishVersion',
216
+ 'lambda:CreateAlias',
217
+ 'lambda:UpdateAlias',
218
+ 'lambda:DeleteAlias',
219
+ 'lambda:GetAlias',
220
+ 'lambda:AddPermission',
221
+ 'lambda:RemovePermission',
222
+ 'lambda:GetPolicy',
223
+ 'lambda:PutProvisionedConcurrencyConfig',
224
+ 'lambda:DeleteProvisionedConcurrencyConfig',
225
+ 'lambda:PutConcurrency',
226
+ 'lambda:DeleteConcurrency',
227
+ 'lambda:TagResource',
228
+ 'lambda:UntagResource',
229
+ 'lambda:ListVersionsByFunction',
230
+
231
+ // IAM permissions
232
+ 'iam:CreateRole',
233
+ 'iam:DeleteRole',
234
+ 'iam:GetRole',
235
+ 'iam:PassRole',
236
+ 'iam:PutRolePolicy',
237
+ 'iam:DeleteRolePolicy',
238
+ 'iam:GetRolePolicy',
239
+ 'iam:AttachRolePolicy',
240
+ 'iam:DetachRolePolicy',
241
+ 'iam:TagRole',
242
+ 'iam:UntagRole',
243
+ 'iam:ListPolicyVersions',
244
+
245
+ // S3 permissions
246
+ 's3:CreateBucket',
247
+ 's3:PutObject',
248
+ 's3:GetObject',
249
+ 's3:DeleteObject',
250
+ 's3:PutBucketPolicy',
251
+ 's3:PutBucketVersioning',
252
+ 's3:PutBucketPublicAccessBlock',
253
+ 's3:GetBucketLocation',
254
+ 's3:ListBucket',
255
+
256
+ // SQS permissions
257
+ 'sqs:CreateQueue',
258
+ 'sqs:DeleteQueue',
259
+ 'sqs:GetQueueAttributes',
260
+ 'sqs:SetQueueAttributes',
261
+ 'sqs:GetQueueUrl',
262
+ 'sqs:TagQueue',
263
+ 'sqs:UntagQueue',
264
+
265
+ // SNS permissions
266
+ 'sns:CreateTopic',
267
+ 'sns:DeleteTopic',
268
+ 'sns:GetTopicAttributes',
269
+ 'sns:SetTopicAttributes',
270
+ 'sns:Subscribe',
271
+ 'sns:Unsubscribe',
272
+ 'sns:ListSubscriptionsByTopic',
273
+ 'sns:TagResource',
274
+ 'sns:UntagResource',
275
+
276
+ // CloudWatch and Logs permissions
277
+ 'cloudwatch:PutMetricAlarm',
278
+ 'cloudwatch:DeleteAlarms',
279
+ 'cloudwatch:DescribeAlarms',
280
+ 'logs:CreateLogGroup',
281
+ 'logs:CreateLogStream',
282
+ 'logs:DeleteLogGroup',
283
+ 'logs:DescribeLogGroups',
284
+ 'logs:DescribeLogStreams',
285
+ 'logs:FilterLogEvents',
286
+ 'logs:PutLogEvents',
287
+ 'logs:PutRetentionPolicy',
288
+
289
+ // API Gateway permissions
290
+ 'apigateway:POST',
291
+ 'apigateway:PUT',
292
+ 'apigateway:DELETE',
293
+ 'apigateway:GET',
294
+ 'apigateway:PATCH',
295
+ 'apigateway:TagResource',
296
+ 'apigateway:UntagResource',
297
+ ];
298
+
299
+ const coreStatements = [
300
+ {
301
+ Sid: 'CloudFormationFriggStacks',
302
+ Effect: 'Allow',
303
+ Action: [
304
+ 'cloudformation:CreateStack',
305
+ 'cloudformation:UpdateStack',
306
+ 'cloudformation:DeleteStack',
307
+ 'cloudformation:DescribeStacks',
308
+ 'cloudformation:DescribeStackEvents',
309
+ 'cloudformation:DescribeStackResources',
310
+ 'cloudformation:DescribeStackResource',
311
+ 'cloudformation:ListStackResources',
312
+ 'cloudformation:GetTemplate',
313
+ 'cloudformation:DescribeChangeSet',
314
+ 'cloudformation:CreateChangeSet',
315
+ 'cloudformation:DeleteChangeSet',
316
+ 'cloudformation:ExecuteChangeSet',
317
+ ],
318
+ Resource: [
319
+ {
320
+ 'Fn::Sub':
321
+ 'arn:aws:cloudformation:*:${AWS::AccountId}:stack/*frigg*/*',
322
+ },
323
+ ],
324
+ },
325
+ {
326
+ Sid: 'CloudFormationValidateTemplate',
327
+ Effect: 'Allow',
328
+ Action: ['cloudformation:ValidateTemplate'],
329
+ Resource: '*',
330
+ },
331
+ {
332
+ Sid: 'S3DeploymentBucket',
333
+ Effect: 'Allow',
334
+ Action: [
335
+ 's3:CreateBucket',
336
+ 's3:PutObject',
337
+ 's3:GetObject',
338
+ 's3:DeleteObject',
339
+ 's3:PutBucketPolicy',
340
+ 's3:PutBucketVersioning',
341
+ 's3:PutBucketPublicAccessBlock',
342
+ 's3:GetBucketLocation',
343
+ 's3:ListBucket',
344
+ ],
345
+ Resource: [
346
+ 'arn:aws:s3:::*serverless*',
347
+ 'arn:aws:s3:::*serverless*/*',
348
+ ],
349
+ },
350
+ {
351
+ Sid: 'LambdaFriggFunctions',
352
+ Effect: 'Allow',
353
+ Action: [
354
+ 'lambda:CreateFunction',
355
+ 'lambda:UpdateFunctionCode',
356
+ 'lambda:UpdateFunctionConfiguration',
357
+ 'lambda:DeleteFunction',
358
+ 'lambda:GetFunction',
359
+ 'lambda:ListFunctions',
360
+ 'lambda:PublishVersion',
361
+ 'lambda:CreateAlias',
362
+ 'lambda:UpdateAlias',
363
+ 'lambda:DeleteAlias',
364
+ 'lambda:GetAlias',
365
+ 'lambda:AddPermission',
366
+ 'lambda:RemovePermission',
367
+ 'lambda:GetPolicy',
368
+ 'lambda:PutProvisionedConcurrencyConfig',
369
+ 'lambda:DeleteProvisionedConcurrencyConfig',
370
+ 'lambda:PutConcurrency',
371
+ 'lambda:DeleteConcurrency',
372
+ 'lambda:TagResource',
373
+ 'lambda:UntagResource',
374
+ 'lambda:ListVersionsByFunction',
375
+ ],
376
+ Resource: [
377
+ {
378
+ 'Fn::Sub':
379
+ 'arn:aws:lambda:*:${AWS::AccountId}:function:*frigg*',
380
+ },
381
+ ],
382
+ },
383
+ {
384
+ Sid: 'IAMRolesForFriggLambda',
385
+ Effect: 'Allow',
386
+ Action: [
387
+ 'iam:CreateRole',
388
+ 'iam:DeleteRole',
389
+ 'iam:GetRole',
390
+ 'iam:PassRole',
391
+ 'iam:PutRolePolicy',
392
+ 'iam:DeleteRolePolicy',
393
+ 'iam:GetRolePolicy',
394
+ 'iam:AttachRolePolicy',
395
+ 'iam:DetachRolePolicy',
396
+ 'iam:TagRole',
397
+ 'iam:UntagRole',
398
+ ],
399
+ Resource: [
400
+ { 'Fn::Sub': 'arn:aws:iam::${AWS::AccountId}:role/*frigg*' },
401
+ {
402
+ 'Fn::Sub':
403
+ 'arn:aws:iam::${AWS::AccountId}:role/*frigg*LambdaRole*',
404
+ },
405
+ ],
406
+ },
407
+ {
408
+ Sid: 'IAMPolicyVersionPermissions',
409
+ Effect: 'Allow',
410
+ Action: ['iam:ListPolicyVersions'],
411
+ Resource: [
412
+ { 'Fn::Sub': 'arn:aws:iam::${AWS::AccountId}:policy/*' },
413
+ ],
414
+ },
415
+ {
416
+ Sid: 'FriggMessagingServices',
417
+ Effect: 'Allow',
418
+ Action: [
419
+ 'sqs:CreateQueue',
420
+ 'sqs:DeleteQueue',
421
+ 'sqs:GetQueueAttributes',
422
+ 'sqs:SetQueueAttributes',
423
+ 'sqs:GetQueueUrl',
424
+ 'sqs:TagQueue',
425
+ 'sqs:UntagQueue',
426
+ ],
427
+ Resource: [
428
+ { 'Fn::Sub': 'arn:aws:sqs:*:${AWS::AccountId}:*frigg*' },
429
+ {
430
+ 'Fn::Sub':
431
+ 'arn:aws:sqs:*:${AWS::AccountId}:internal-error-queue-*',
432
+ },
433
+ ],
434
+ },
435
+ {
436
+ Sid: 'FriggSNSTopics',
437
+ Effect: 'Allow',
438
+ Action: [
439
+ 'sns:CreateTopic',
440
+ 'sns:DeleteTopic',
441
+ 'sns:GetTopicAttributes',
442
+ 'sns:SetTopicAttributes',
443
+ 'sns:Subscribe',
444
+ 'sns:Unsubscribe',
445
+ 'sns:ListSubscriptionsByTopic',
446
+ 'sns:TagResource',
447
+ 'sns:UntagResource',
448
+ ],
449
+ Resource: [
450
+ { 'Fn::Sub': 'arn:aws:sns:*:${AWS::AccountId}:*frigg*' },
451
+ ],
452
+ },
453
+ {
454
+ Sid: 'FriggMonitoringAndLogs',
455
+ Effect: 'Allow',
456
+ Action: [
457
+ 'cloudwatch:PutMetricAlarm',
458
+ 'cloudwatch:DeleteAlarms',
459
+ 'cloudwatch:DescribeAlarms',
460
+ 'logs:CreateLogGroup',
461
+ 'logs:CreateLogStream',
462
+ 'logs:DeleteLogGroup',
463
+ 'logs:DescribeLogGroups',
464
+ 'logs:DescribeLogStreams',
465
+ 'logs:FilterLogEvents',
466
+ 'logs:PutLogEvents',
467
+ 'logs:PutRetentionPolicy',
468
+ ],
469
+ Resource: [
470
+ {
471
+ 'Fn::Sub':
472
+ 'arn:aws:logs:*:${AWS::AccountId}:log-group:/aws/lambda/*frigg*',
473
+ },
474
+ {
475
+ 'Fn::Sub':
476
+ 'arn:aws:logs:*:${AWS::AccountId}:log-group:/aws/lambda/*frigg*:*',
477
+ },
478
+ {
479
+ 'Fn::Sub':
480
+ 'arn:aws:cloudwatch:*:${AWS::AccountId}:alarm:*frigg*',
481
+ },
482
+ ],
483
+ },
484
+ {
485
+ Sid: 'FriggAPIGateway',
486
+ Effect: 'Allow',
487
+ Action: [
488
+ 'apigateway:POST',
489
+ 'apigateway:PUT',
490
+ 'apigateway:DELETE',
491
+ 'apigateway:GET',
492
+ 'apigateway:PATCH',
493
+ 'apigateway:TagResource',
494
+ 'apigateway:UntagResource',
495
+ ],
496
+ Resource: [
497
+ 'arn:aws:apigateway:*::/restapis',
498
+ 'arn:aws:apigateway:*::/restapis/*',
499
+ 'arn:aws:apigateway:*::/domainnames',
500
+ 'arn:aws:apigateway:*::/domainnames/*',
501
+ ],
502
+ },
503
+ {
504
+ Sid: 'FriggAPIGatewayV2',
505
+ Effect: 'Allow',
506
+ Action: [
507
+ 'apigateway:GET',
508
+ 'apigateway:DELETE',
509
+ 'apigateway:PATCH',
510
+ 'apigateway:POST',
511
+ 'apigateway:PUT',
512
+ ],
513
+ Resource: [
514
+ 'arn:aws:apigateway:*::/apis',
515
+ 'arn:aws:apigateway:*::/apis/*',
516
+ 'arn:aws:apigateway:*::/apis/*/stages',
517
+ 'arn:aws:apigateway:*::/apis/*/stages/*',
518
+ 'arn:aws:apigateway:*::/domainnames',
519
+ 'arn:aws:apigateway:*::/domainnames/*',
520
+ 'arn:aws:apigateway:*::/domainnames/*/apimappings',
521
+ ],
522
+ },
523
+ ];
524
+
525
+ template.Resources.FriggCoreDeploymentPolicy = {
526
+ Type: 'AWS::IAM::ManagedPolicy',
527
+ Properties: {
528
+ ManagedPolicyName: 'FriggCoreDeploymentPolicy',
529
+ Description: 'Core permissions for deploying Frigg applications',
530
+ PolicyDocument: {
531
+ Version: '2012-10-17',
532
+ Statement: coreStatements,
533
+ },
534
+ },
535
+ };
536
+
537
+ // Add feature-specific policies only if needed
538
+ if (features.vpc) {
539
+ template.Resources.FriggVPCPolicy = {
540
+ Type: 'AWS::IAM::ManagedPolicy',
541
+ Condition: 'CreateVPCPermissions',
542
+ Properties: {
543
+ ManagedPolicyName: 'FriggVPCPolicy',
544
+ Description: 'VPC-related permissions for Frigg applications',
545
+ PolicyDocument: {
546
+ Version: '2012-10-17',
547
+ Statement: [
548
+ {
549
+ Sid: 'FriggVPCEndpointManagement',
550
+ Effect: 'Allow',
551
+ Action: [
552
+ 'ec2:CreateVpcEndpoint',
553
+ 'ec2:DeleteVpcEndpoint',
554
+ 'ec2:DescribeVpcEndpoints',
555
+ 'ec2:ModifyVpcEndpoint',
556
+ 'ec2:CreateNatGateway',
557
+ 'ec2:DeleteNatGateway',
558
+ 'ec2:DescribeNatGateways',
559
+ 'ec2:AllocateAddress',
560
+ 'ec2:ReleaseAddress',
561
+ 'ec2:DescribeAddresses',
562
+ 'ec2:CreateRouteTable',
563
+ 'ec2:DeleteRouteTable',
564
+ 'ec2:DescribeRouteTables',
565
+ 'ec2:CreateRoute',
566
+ 'ec2:DeleteRoute',
567
+ 'ec2:ReplaceRoute',
568
+ 'ec2:AssociateRouteTable',
569
+ 'ec2:DisassociateRouteTable',
570
+ 'ec2:CreateSecurityGroup',
571
+ 'ec2:DeleteSecurityGroup',
572
+ 'ec2:AuthorizeSecurityGroupEgress',
573
+ 'ec2:AuthorizeSecurityGroupIngress',
574
+ 'ec2:RevokeSecurityGroupEgress',
575
+ 'ec2:RevokeSecurityGroupIngress',
576
+ 'ec2:DetachInternetGateway',
577
+ 'ec2:DeleteSubnet',
578
+ ],
579
+ Resource: '*',
580
+ },
581
+ ],
582
+ },
583
+ },
584
+ };
585
+ }
586
+
587
+ if (features.kms) {
588
+ template.Resources.FriggKMSPolicy = {
589
+ Type: 'AWS::IAM::ManagedPolicy',
590
+ Condition: 'CreateKMSPermissions',
591
+ Properties: {
592
+ ManagedPolicyName: 'FriggKMSPolicy',
593
+ Description:
594
+ 'KMS encryption permissions for Frigg applications',
595
+ PolicyDocument: {
596
+ Version: '2012-10-17',
597
+ Statement: [
598
+ {
599
+ Sid: 'FriggKMSEncryptionRuntime',
600
+ Effect: 'Allow',
601
+ Action: ['kms:GenerateDataKey', 'kms:Decrypt'],
602
+ Resource: [
603
+ {
604
+ 'Fn::Sub':
605
+ 'arn:aws:kms:*:${AWS::AccountId}:key/*',
606
+ },
607
+ ],
608
+ Condition: {
609
+ StringEquals: {
610
+ 'kms:ViaService': [
611
+ 'lambda.*.amazonaws.com',
612
+ 's3.*.amazonaws.com',
613
+ ],
614
+ },
615
+ },
616
+ },
617
+ {
618
+ Sid: 'FriggKMSManagement',
619
+ Effect: 'Allow',
620
+ Action: [
621
+ 'kms:CreateKey',
622
+ 'kms:PutKeyPolicy',
623
+ 'kms:EnableKeyRotation',
624
+ 'kms:TagResource',
625
+ 'kms:UntagResource',
626
+ 'kms:ListResourceTags',
627
+ 'kms:CreateAlias',
628
+ 'kms:UpdateAlias',
629
+ 'kms:DeleteAlias',
630
+ 'kms:ListAliases',
631
+ 'kms:DescribeKey',
632
+ ],
633
+ Resource: '*',
634
+ },
635
+ ],
636
+ },
637
+ },
638
+ };
639
+ }
640
+
641
+ template.Resources.FriggKMSKeyAlias = {
642
+ Type: 'AWS::KMS::Alias',
643
+ Condition: 'CreateKMSAlias',
644
+ DeletionPolicy: 'Retain',
645
+ UpdateReplacePolicy: 'Retain',
646
+ Properties: {
647
+ AliasName: { Ref: 'DeploymentKmsAliasName' },
648
+ TargetKeyId: { Ref: 'DeploymentKmsTargetKeyArn' },
649
+ },
650
+ };
651
+
652
+ if (features.ssm) {
653
+ template.Resources.FriggSSMPolicy = {
654
+ Type: 'AWS::IAM::ManagedPolicy',
655
+ Condition: 'CreateSSMPermissions',
656
+ Properties: {
657
+ ManagedPolicyName: 'FriggSSMPolicy',
658
+ Description:
659
+ 'SSM Parameter Store permissions for Frigg applications',
660
+ PolicyDocument: {
661
+ Version: '2012-10-17',
662
+ Statement: [
663
+ {
664
+ Sid: 'FriggSSMParameterAccess',
665
+ Effect: 'Allow',
666
+ Action: [
667
+ 'ssm:GetParameter',
668
+ 'ssm:GetParameters',
669
+ 'ssm:GetParametersByPath',
670
+ ],
671
+ Resource: [
672
+ {
673
+ 'Fn::Sub':
674
+ 'arn:aws:ssm:*:${AWS::AccountId}:parameter/*frigg*',
675
+ },
676
+ {
677
+ 'Fn::Sub':
678
+ 'arn:aws:ssm:*:${AWS::AccountId}:parameter/*frigg*/*',
679
+ },
680
+ ],
681
+ },
682
+ ],
683
+ },
684
+ },
685
+ };
686
+ }
687
+
688
+ // Add Secrets Manager for credentials
689
+ template.Resources.FriggDeploymentCredentials = {
690
+ Type: 'AWS::SecretsManager::Secret',
691
+ Properties: {
692
+ Name: 'frigg-deployment-credentials',
693
+ Description: 'Access credentials for Frigg deployment user',
694
+ SecretString: {
695
+ 'Fn::Sub': JSON.stringify({
696
+ AccessKeyId: '${FriggDeploymentAccessKey}',
697
+ SecretAccessKey:
698
+ '${FriggDeploymentAccessKey.SecretAccessKey}',
699
+ }),
700
+ },
701
+ },
702
+ };
703
+
704
+ // Add Outputs
705
+ template.Outputs = {
706
+ DeploymentUserArn: {
707
+ Description: 'ARN of the Frigg deployment user',
708
+ Value: { 'Fn::GetAtt': ['FriggDeploymentUser', 'Arn'] },
709
+ Export: {
710
+ Name: { 'Fn::Sub': '${AWS::StackName}-UserArn' },
711
+ },
712
+ },
713
+ AccessKeyId: {
714
+ Description: 'Access Key ID for the deployment user',
715
+ Value: { Ref: 'FriggDeploymentAccessKey' },
716
+ Export: {
717
+ Name: { 'Fn::Sub': '${AWS::StackName}-AccessKeyId' },
718
+ },
719
+ },
720
+ SecretAccessKeyCommand: {
721
+ Description: 'Command to retrieve the secret access key',
722
+ Value: {
723
+ 'Fn::Sub':
724
+ 'aws secretsmanager get-secret-value --secret-id frigg-deployment-credentials --query SecretString --output text | jq -r .SecretAccessKey',
725
+ },
726
+ },
727
+ CredentialsSecretArn: {
728
+ Description: 'ARN of the secret containing deployment credentials',
729
+ Value: { Ref: 'FriggDeploymentCredentials' },
730
+ Export: {
731
+ Name: { 'Fn::Sub': '${AWS::StackName}-CredentialsSecretArn' },
732
+ },
733
+ },
734
+ };
735
+
736
+ // Convert to YAML
737
+ return convertToYAML(template);
738
+ }
739
+
740
+ /**
741
+ * Convert JavaScript object to CloudFormation YAML
742
+ * @param {Object} obj - JavaScript object
743
+ * @returns {string} YAML string
744
+ */
745
+ function convertToYAML(obj) {
746
+ const yaml = require('js-yaml');
747
+ return yaml.dump(obj, {
748
+ indent: 2,
749
+ lineWidth: 120,
750
+ noRefs: true,
751
+ sortKeys: false,
752
+ });
753
+ }
754
+
755
+ /**
756
+ * Generate summary of what features will be included in the IAM policy
757
+ * @param {Object} appDefinition - Application definition
758
+ * @returns {Object} Feature summary
759
+ */
760
+ function getFeatureSummary(appDefinition) {
761
+ const features = {
762
+ core: true, // Always enabled
763
+ vpc: appDefinition.vpc?.enable === true,
764
+ kms:
765
+ appDefinition.encryption?.fieldLevelEncryptionMethod === 'kms',
766
+ ssm: appDefinition.ssm?.enable === true,
767
+ websockets: appDefinition.websockets?.enable === true,
768
+ };
769
+
770
+ const integrationCount = appDefinition.integrations?.length || 0;
771
+
772
+ return {
773
+ features,
774
+ integrationCount,
775
+ appName: appDefinition.name || 'Unnamed Frigg App',
776
+ };
777
+ }
778
+
779
+ /**
780
+ * Generate basic IAM policy (JSON format) - Core Frigg permissions only
781
+ * @returns {Object} Basic IAM policy document
782
+ */
783
+ function generateBasicIAMPolicy() {
784
+ const basicPolicyPath = path.join(__dirname, 'templates/iam-policy-basic.json');
785
+ return require(basicPolicyPath);
786
+ }
787
+
788
+ /**
789
+ * Generate full IAM policy (JSON format) - All features enabled
790
+ * @returns {Object} Full IAM policy document
791
+ */
792
+ function generateFullIAMPolicy() {
793
+ const fullPolicyPath = path.join(__dirname, 'templates/iam-policy-full.json');
794
+ return require(fullPolicyPath);
795
+ }
796
+
797
+ /**
798
+ * Generate IAM policy based on mode
799
+ * @param {string} mode - 'basic' or 'full'
800
+ * @returns {Object} IAM policy document
801
+ */
802
+ function generateIAMPolicy(mode = 'basic') {
803
+ if (mode === 'full') {
804
+ return generateFullIAMPolicy();
805
+ }
806
+ return generateBasicIAMPolicy();
807
+ }
808
+
809
+ module.exports = {
810
+ generateIAMCloudFormation,
811
+ generateCloudFormationTemplate: generateIAMCloudFormation, // Alias for generate-command/index.js compatibility
812
+ getFeatureSummary,
813
+ generateBasicIAMPolicy,
814
+ generateFullIAMPolicy,
815
+ generateIAMPolicy,
816
+ };