@friggframework/devtools 2.0.0-next.32 → 2.0.0-next.34

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.
@@ -17,38 +17,39 @@ These permissions are required when `aws-discovery.js` runs during the build to
17
17
 
18
18
  ```json
19
19
  {
20
- "Version": "2012-10-17",
21
- "Statement": [
22
- {
23
- "Sid": "AWSDiscoveryPermissions",
24
- "Effect": "Allow",
25
- "Action": [
26
- "sts:GetCallerIdentity",
27
- "ec2:DescribeVpcs",
28
- "ec2:DescribeSubnets",
29
- "ec2:DescribeSecurityGroups",
30
- "ec2:DescribeRouteTables",
31
- "ec2:DescribeNatGateways",
32
- "ec2:DescribeAddresses",
33
- "kms:ListKeys",
34
- "kms:DescribeKey"
35
- ],
36
- "Resource": "*"
37
- }
38
- ]
20
+ "Version": "2012-10-17",
21
+ "Statement": [
22
+ {
23
+ "Sid": "AWSDiscoveryPermissions",
24
+ "Effect": "Allow",
25
+ "Action": [
26
+ "sts:GetCallerIdentity",
27
+ "ec2:DescribeVpcs",
28
+ "ec2:DescribeSubnets",
29
+ "ec2:DescribeSecurityGroups",
30
+ "ec2:DescribeRouteTables",
31
+ "ec2:DescribeNatGateways",
32
+ "ec2:DescribeAddresses",
33
+ "kms:ListKeys",
34
+ "kms:DescribeKey"
35
+ ],
36
+ "Resource": "*"
37
+ }
38
+ ]
39
39
  }
40
40
  ```
41
41
 
42
42
  ### What Each Permission Does:
43
- - **`sts:GetCallerIdentity`** - Gets your AWS account ID for KMS key ARN construction
44
- - **`ec2:DescribeVpcs`** - Finds your default VPC or first available VPC
45
- - **`ec2:DescribeSubnets`** - Identifies private subnets within your VPC
46
- - **`ec2:DescribeSecurityGroups`** - Locates default security group or Frigg-specific security group
47
- - **`ec2:DescribeRouteTables`** - Determines which subnets are private (no direct internet gateway route)
48
- - **`ec2:DescribeNatGateways`** - Finds existing NAT Gateways to reuse (prevents duplicate resource creation)
49
- - **`ec2:DescribeAddresses`** - Finds available Elastic IPs to reuse (prevents allocation conflicts)
50
- - **`kms:ListKeys`** - Lists available KMS keys in your account
51
- - **`kms:DescribeKey`** - Gets details about KMS keys to find customer-managed keys
43
+
44
+ - **`sts:GetCallerIdentity`** - Gets your AWS account ID for KMS key ARN construction
45
+ - **`ec2:DescribeVpcs`** - Finds your default VPC or first available VPC
46
+ - **`ec2:DescribeSubnets`** - Identifies private subnets within your VPC
47
+ - **`ec2:DescribeSecurityGroups`** - Locates default security group or Frigg-specific security group
48
+ - **`ec2:DescribeRouteTables`** - Determines which subnets are private (no direct internet gateway route)
49
+ - **`ec2:DescribeNatGateways`** - Finds existing NAT Gateways to reuse (prevents duplicate resource creation)
50
+ - **`ec2:DescribeAddresses`** - Finds available Elastic IPs to reuse (prevents allocation conflicts)
51
+ - **`kms:ListKeys`** - Lists available KMS keys in your account
52
+ - **`kms:DescribeKey`** - Gets details about KMS keys to find customer-managed keys
52
53
 
53
54
  ## Core Deployment Permissions
54
55
 
@@ -56,214 +57,236 @@ Required for basic Frigg application deployment:
56
57
 
57
58
  ```json
58
59
  {
59
- "Version": "2012-10-17",
60
- "Statement": [
61
- {
62
- "Sid": "CloudFormationFriggStacks",
63
- "Effect": "Allow",
64
- "Action": [
65
- "cloudformation:CreateStack",
66
- "cloudformation:UpdateStack",
67
- "cloudformation:DeleteStack",
68
- "cloudformation:DescribeStacks",
69
- "cloudformation:DescribeStackEvents",
70
- "cloudformation:DescribeStackResources",
71
- "cloudformation:DescribeStackResource",
72
- "cloudformation:ListStackResources",
73
- "cloudformation:GetTemplate",
74
- "cloudformation:ValidateTemplate",
75
- "cloudformation:DescribeChangeSet",
76
- "cloudformation:CreateChangeSet",
77
- "cloudformation:DeleteChangeSet",
78
- "cloudformation:ExecuteChangeSet"
79
- ],
80
- "Resource": [
81
- "arn:aws:cloudformation:*:*:stack/*frigg*/*"
82
- ]
83
- },
84
- {
85
- "Sid": "S3DeploymentBucket",
86
- "Effect": "Allow",
87
- "Action": [
88
- "s3:CreateBucket",
89
- "s3:PutObject",
90
- "s3:GetObject",
91
- "s3:DeleteObject",
92
- "s3:PutBucketPolicy",
93
- "s3:PutBucketVersioning",
94
- "s3:PutBucketPublicAccessBlock",
95
- "s3:GetBucketLocation",
96
- "s3:ListBucket",
97
- "s3:PutBucketTagging",
98
- "s3:GetBucketTagging"
99
- ],
100
- "Resource": [
101
- "arn:aws:s3:::*serverless*",
102
- "arn:aws:s3:::*serverless*/*"
103
- ]
104
- },
105
- {
106
- "Sid": "LambdaFriggFunctions",
107
- "Effect": "Allow",
108
- "Action": [
109
- "lambda:CreateFunction",
110
- "lambda:UpdateFunctionCode",
111
- "lambda:UpdateFunctionConfiguration",
112
- "lambda:DeleteFunction",
113
- "lambda:GetFunction",
114
- "lambda:ListFunctions",
115
- "lambda:PublishVersion",
116
- "lambda:CreateAlias",
117
- "lambda:UpdateAlias",
118
- "lambda:DeleteAlias",
119
- "lambda:GetAlias",
120
- "lambda:AddPermission",
121
- "lambda:RemovePermission",
122
- "lambda:GetPolicy",
123
- "lambda:PutProvisionedConcurrencyConfig",
124
- "lambda:DeleteProvisionedConcurrencyConfig",
125
- "lambda:PutConcurrency",
126
- "lambda:DeleteConcurrency",
127
- "lambda:TagResource",
128
- "lambda:UntagResource",
129
- "lambda:ListVersionsByFunction"
130
- ],
131
- "Resource": [
132
- "arn:aws:lambda:*:*:function:*frigg*"
133
- ]
134
- },
135
- {
136
- "Sid": "FriggLambdaEventSourceMapping",
137
- "Effect": "Allow",
138
- "Action": [
139
- "lambda:CreateEventSourceMapping",
140
- "lambda:DeleteEventSourceMapping",
141
- "lambda:GetEventSourceMapping",
142
- "lambda:UpdateEventSourceMapping",
143
- "lambda:ListEventSourceMappings"
144
- ],
145
- "Resource": [
146
- "arn:aws:lambda:*:*:event-source-mapping:*"
147
- ]
148
- },
149
- {
150
- "Sid": "IAMRolesForFriggLambda",
151
- "Effect": "Allow",
152
- "Action": [
153
- "iam:CreateRole",
154
- "iam:DeleteRole",
155
- "iam:GetRole",
156
- "iam:PassRole",
157
- "iam:PutRolePolicy",
158
- "iam:DeleteRolePolicy",
159
- "iam:GetRolePolicy",
160
- "iam:AttachRolePolicy",
161
- "iam:DetachRolePolicy",
162
- "iam:TagRole",
163
- "iam:UntagRole"
164
- ],
165
- "Resource": [
166
- "arn:aws:iam::*:role/*frigg*",
167
- "arn:aws:iam::*:role/*frigg*LambdaRole*"
168
- ]
169
- },
170
- {
171
- "Sid": "IAMPolicyVersionPermissions",
172
- "Effect": "Allow",
173
- "Action": [
174
- "iam:ListPolicyVersions"
175
- ],
176
- "Resource": [
177
- "arn:aws:iam::*:policy/*"
178
- ]
179
- },
180
- {
181
- "Sid": "FriggMessagingServices",
182
- "Effect": "Allow",
183
- "Action": [
184
- "sqs:CreateQueue",
185
- "sqs:DeleteQueue",
186
- "sqs:GetQueueAttributes",
187
- "sqs:SetQueueAttributes",
188
- "sqs:GetQueueUrl",
189
- "sqs:TagQueue",
190
- "sqs:UntagQueue"
191
- ],
192
- "Resource": [
193
- "arn:aws:sqs:*:*:*frigg*",
194
- "arn:aws:sqs:*:*:internal-error-queue-*"
195
- ]
196
- },
197
- {
198
- "Sid": "FriggSNSTopics",
199
- "Effect": "Allow",
200
- "Action": [
201
- "sns:CreateTopic",
202
- "sns:DeleteTopic",
203
- "sns:GetTopicAttributes",
204
- "sns:SetTopicAttributes",
205
- "sns:Subscribe",
206
- "sns:Unsubscribe",
207
- "sns:TagResource",
208
- "sns:UntagResource"
209
- ],
210
- "Resource": [
211
- "arn:aws:sns:*:*:*frigg*"
212
- ]
213
- },
214
- {
215
- "Sid": "FriggMonitoringAndLogs",
216
- "Effect": "Allow",
217
- "Action": [
218
- "cloudwatch:PutMetricAlarm",
219
- "cloudwatch:DeleteAlarms",
220
- "cloudwatch:DescribeAlarms",
221
- "logs:CreateLogGroup",
222
- "logs:CreateLogStream",
223
- "logs:DeleteLogGroup",
224
- "logs:DescribeLogGroups",
225
- "logs:DescribeLogStreams",
226
- "logs:FilterLogEvents",
227
- "logs:PutLogEvents",
228
- "logs:PutRetentionPolicy"
229
- ],
230
- "Resource": [
231
- "arn:aws:logs:*:*:log-group:/aws/lambda/*frigg*",
232
- "arn:aws:logs:*:*:log-group:/aws/lambda/*frigg*:*",
233
- "arn:aws:cloudwatch:*:*:alarm:*frigg*"
234
- ]
235
- },
236
- {
237
- "Sid": "FriggAPIGateway",
238
- "Effect": "Allow",
239
- "Action": [
240
- "apigateway:POST",
241
- "apigateway:PUT",
242
- "apigateway:DELETE",
243
- "apigateway:GET",
244
- "apigateway:PATCH"
245
- ],
246
- "Resource": [
247
- "arn:aws:apigateway:*::/restapis",
248
- "arn:aws:apigateway:*::/restapis/*",
249
- "arn:aws:apigateway:*::/domainnames",
250
- "arn:aws:apigateway:*::/domainnames/*"
251
- ]
252
- }
253
- ]
60
+ "Version": "2012-10-17",
61
+ "Statement": [
62
+ {
63
+ "Sid": "CloudFormationFriggStacks",
64
+ "Effect": "Allow",
65
+ "Action": [
66
+ "cloudformation:CreateStack",
67
+ "cloudformation:UpdateStack",
68
+ "cloudformation:DeleteStack",
69
+ "cloudformation:DescribeStacks",
70
+ "cloudformation:DescribeStackEvents",
71
+ "cloudformation:DescribeStackResources",
72
+ "cloudformation:DescribeStackResource",
73
+ "cloudformation:ListStackResources",
74
+ "cloudformation:GetTemplate",
75
+ "cloudformation:ValidateTemplate",
76
+ "cloudformation:DescribeChangeSet",
77
+ "cloudformation:CreateChangeSet",
78
+ "cloudformation:DeleteChangeSet",
79
+ "cloudformation:ExecuteChangeSet"
80
+ ],
81
+ "Resource": ["arn:aws:cloudformation:*:*:stack/*frigg*/*"]
82
+ },
83
+ {
84
+ "Sid": "S3DeploymentBucket",
85
+ "Effect": "Allow",
86
+ "Action": [
87
+ "s3:CreateBucket",
88
+ "s3:PutObject",
89
+ "s3:GetObject",
90
+ "s3:DeleteObject",
91
+ "s3:PutBucketPolicy",
92
+ "s3:PutBucketVersioning",
93
+ "s3:PutBucketPublicAccessBlock",
94
+ "s3:GetBucketLocation",
95
+ "s3:ListBucket",
96
+ "s3:PutBucketTagging",
97
+ "s3:GetBucketTagging"
98
+ ],
99
+ "Resource": [
100
+ "arn:aws:s3:::*serverless*",
101
+ "arn:aws:s3:::*serverless*/*"
102
+ ]
103
+ },
104
+ {
105
+ "Sid": "LambdaFriggFunctions",
106
+ "Effect": "Allow",
107
+ "Action": [
108
+ "lambda:CreateFunction",
109
+ "lambda:UpdateFunctionCode",
110
+ "lambda:UpdateFunctionConfiguration",
111
+ "lambda:DeleteFunction",
112
+ "lambda:GetFunction",
113
+ "lambda:ListFunctions",
114
+ "lambda:PublishVersion",
115
+ "lambda:CreateAlias",
116
+ "lambda:UpdateAlias",
117
+ "lambda:DeleteAlias",
118
+ "lambda:GetAlias",
119
+ "lambda:AddPermission",
120
+ "lambda:RemovePermission",
121
+ "lambda:GetPolicy",
122
+ "lambda:PutProvisionedConcurrencyConfig",
123
+ "lambda:DeleteProvisionedConcurrencyConfig",
124
+ "lambda:PutConcurrency",
125
+ "lambda:DeleteConcurrency",
126
+ "lambda:TagResource",
127
+ "lambda:UntagResource",
128
+ "lambda:ListVersionsByFunction"
129
+ ],
130
+ "Resource": ["arn:aws:lambda:*:*:function:*frigg*"]
131
+ },
132
+ {
133
+ "Sid": "FriggLambdaEventSourceMapping",
134
+ "Effect": "Allow",
135
+ "Action": [
136
+ "lambda:CreateEventSourceMapping",
137
+ "lambda:DeleteEventSourceMapping",
138
+ "lambda:GetEventSourceMapping",
139
+ "lambda:UpdateEventSourceMapping",
140
+ "lambda:ListEventSourceMappings"
141
+ ],
142
+ "Resource": ["arn:aws:lambda:*:*:event-source-mapping:*"]
143
+ },
144
+ {
145
+ "Sid": "IAMRolesForFriggLambda",
146
+ "Effect": "Allow",
147
+ "Action": [
148
+ "iam:CreateRole",
149
+ "iam:DeleteRole",
150
+ "iam:GetRole",
151
+ "iam:PassRole",
152
+ "iam:PutRolePolicy",
153
+ "iam:DeleteRolePolicy",
154
+ "iam:GetRolePolicy",
155
+ "iam:AttachRolePolicy",
156
+ "iam:DetachRolePolicy",
157
+ "iam:TagRole",
158
+ "iam:UntagRole"
159
+ ],
160
+ "Resource": [
161
+ "arn:aws:iam::*:role/*frigg*",
162
+ "arn:aws:iam::*:role/*frigg*LambdaRole*"
163
+ ]
164
+ },
165
+ {
166
+ "Sid": "IAMPolicyVersionPermissions",
167
+ "Effect": "Allow",
168
+ "Action": ["iam:ListPolicyVersions"],
169
+ "Resource": ["arn:aws:iam::*:policy/*"]
170
+ },
171
+ {
172
+ "Sid": "FriggMessagingServices",
173
+ "Effect": "Allow",
174
+ "Action": [
175
+ "sqs:CreateQueue",
176
+ "sqs:DeleteQueue",
177
+ "sqs:GetQueueAttributes",
178
+ "sqs:SetQueueAttributes",
179
+ "sqs:GetQueueUrl",
180
+ "sqs:TagQueue",
181
+ "sqs:UntagQueue"
182
+ ],
183
+ "Resource": [
184
+ "arn:aws:sqs:*:*:*frigg*",
185
+ "arn:aws:sqs:*:*:internal-error-queue-*"
186
+ ]
187
+ },
188
+ {
189
+ "Sid": "FriggSNSTopics",
190
+ "Effect": "Allow",
191
+ "Action": [
192
+ "sns:CreateTopic",
193
+ "sns:DeleteTopic",
194
+ "sns:GetTopicAttributes",
195
+ "sns:SetTopicAttributes",
196
+ "sns:Subscribe",
197
+ "sns:Unsubscribe",
198
+ "sns:TagResource",
199
+ "sns:UntagResource"
200
+ ],
201
+ "Resource": ["arn:aws:sns:*:*:*frigg*"]
202
+ },
203
+ {
204
+ "Sid": "FriggMonitoringAndLogs",
205
+ "Effect": "Allow",
206
+ "Action": [
207
+ "cloudwatch:PutMetricAlarm",
208
+ "cloudwatch:DeleteAlarms",
209
+ "cloudwatch:DescribeAlarms",
210
+ "logs:CreateLogGroup",
211
+ "logs:CreateLogStream",
212
+ "logs:DeleteLogGroup",
213
+ "logs:DescribeLogGroups",
214
+ "logs:DescribeLogStreams",
215
+ "logs:FilterLogEvents",
216
+ "logs:PutLogEvents",
217
+ "logs:PutRetentionPolicy"
218
+ ],
219
+ "Resource": [
220
+ "arn:aws:logs:*:*:log-group:/aws/lambda/*frigg*",
221
+ "arn:aws:logs:*:*:log-group:/aws/lambda/*frigg*:*",
222
+ "arn:aws:cloudwatch:*:*:alarm:*frigg*"
223
+ ]
224
+ },
225
+ {
226
+ "Sid": "FriggAPIGateway",
227
+ "Effect": "Allow",
228
+ "Action": [
229
+ "apigateway:POST",
230
+ "apigateway:PUT",
231
+ "apigateway:DELETE",
232
+ "apigateway:GET",
233
+ "apigateway:PATCH"
234
+ ],
235
+ "Resource": [
236
+ "arn:aws:apigateway:*::/restapis",
237
+ "arn:aws:apigateway:*::/restapis/*",
238
+ "arn:aws:apigateway:*::/domainnames",
239
+ "arn:aws:apigateway:*::/domainnames/*"
240
+ ]
241
+ },
242
+ {
243
+ "Sid": "FriggAPIGatewayV2",
244
+ "Effect": "Allow",
245
+ "Action": [
246
+ "apigateway:GET",
247
+ "apigateway:DELETE",
248
+ "apigateway:PATCH",
249
+ "apigateway:POST",
250
+ "apigateway:PUT"
251
+ ],
252
+ "Resource": [
253
+ "arn:aws:apigateway:*::/apis",
254
+ "arn:aws:apigateway:*::/apis/*",
255
+ "arn:aws:apigateway:*::/apis/*/stages",
256
+ "arn:aws:apigateway:*::/apis/*/stages/*",
257
+ "arn:aws:apigateway:*::/apis/*/mappings",
258
+ "arn:aws:apigateway:*::/apis/*/mappings/*"
259
+ ]
260
+ }
261
+ ]
254
262
  }
255
263
  ```
256
264
 
265
+ **API Gateway Permissions Security Note:**
266
+
267
+ The API Gateway permissions are intentionally split into two separate policies:
268
+
269
+ - **FriggAPIGateway**: Covers API Gateway v1 (REST APIs) with specific CRUD operations
270
+ - **FriggAPIGatewayV2**: Covers API Gateway v2 (HTTP APIs) with the same specific operations
271
+
272
+ This approach follows the principle of least privilege by:
273
+
274
+ - ✅ **Avoiding wildcards**: Using specific actions instead of `apigateway:*`
275
+ - ✅ **Resource scoping**: Limiting to Frigg-specific API Gateway resources
276
+ - ✅ **Version separation**: Maintaining clear separation between v1 and v2 APIs
277
+ - ✅ **Audit-friendly**: Each permission can be individually tracked and monitored
278
+
257
279
  **What the Lambda permissions enable:**
258
- - **Function Management**: Create, update, delete, and configure Lambda functions
259
- - **Version & Alias Management**: Publish new versions and manage aliases for deployments
260
- - **Permission Management**: Add/remove function permissions for API Gateway and other services
261
- - **Concurrency Management**: Configure provisioned and reserved concurrency
262
- - **EventSourceMapping Management**: Connect Lambda functions to event sources like SQS, SNS, Kinesis, and DynamoDB streams. These permissions are crucial for:
263
- - Creating mappings between SQS queues and Lambda functions
264
- - Managing event-driven architectures
265
- - Handling queue-based processing (e.g., HubSpot integration queues)
266
- - Cleaning up event source mappings during stack deletion
280
+
281
+ - **Function Management**: Create, update, delete, and configure Lambda functions
282
+ - **Version & Alias Management**: Publish new versions and manage aliases for deployments
283
+ - **Permission Management**: Add/remove function permissions for API Gateway and other services
284
+ - **Concurrency Management**: Configure provisioned and reserved concurrency
285
+ - **EventSourceMapping Management**: Connect Lambda functions to event sources like SQS, SNS, Kinesis, and DynamoDB streams. These permissions are crucial for:
286
+ - Creating mappings between SQS queues and Lambda functions
287
+ - Managing event-driven architectures
288
+ - Handling queue-based processing (e.g., HubSpot integration queues)
289
+ - Cleaning up event source mappings during stack deletion
267
290
 
268
291
  ## Feature-Specific Permissions
269
292
 
@@ -273,58 +296,61 @@ Additional permissions needed when your app definition includes `vpc: { enable:
273
296
 
274
297
  ```json
275
298
  {
276
- "Version": "2012-10-17",
277
- "Statement": [
278
- {
279
- "Sid": "FriggVPCEndpointManagement",
280
- "Effect": "Allow",
281
- "Action": [
282
- "ec2:CreateVpcEndpoint",
283
- "ec2:DeleteVpcEndpoint",
284
- "ec2:DescribeVpcEndpoints",
285
- "ec2:ModifyVpcEndpoint",
286
- "ec2:CreateNatGateway",
287
- "ec2:DeleteNatGateway",
288
- "ec2:DescribeNatGateways",
289
- "ec2:AllocateAddress",
290
- "ec2:ReleaseAddress",
291
- "ec2:DescribeAddresses",
292
- "ec2:CreateRouteTable",
293
- "ec2:DeleteRouteTable",
294
- "ec2:DescribeRouteTables",
295
- "ec2:CreateRoute",
296
- "ec2:DeleteRoute",
297
- "ec2:AssociateRouteTable",
298
- "ec2:DisassociateRouteTable",
299
- "ec2:CreateSecurityGroup",
300
- "ec2:DeleteSecurityGroup",
301
- "ec2:AuthorizeSecurityGroupEgress",
302
- "ec2:AuthorizeSecurityGroupIngress",
303
- "ec2:RevokeSecurityGroupEgress",
304
- "ec2:RevokeSecurityGroupIngress"
305
- ],
306
- "Resource": "*",
307
- "Condition": {
308
- "StringLike": {
309
- "ec2:CreateAction": [
310
- "CreateVpcEndpoint",
311
- "CreateNatGateway",
312
- "CreateRouteTable",
313
- "CreateRoute",
314
- "CreateSecurityGroup"
315
- ]
299
+ "Version": "2012-10-17",
300
+ "Statement": [
301
+ {
302
+ "Sid": "FriggVPCEndpointManagement",
303
+ "Effect": "Allow",
304
+ "Action": [
305
+ "ec2:CreateVpcEndpoint",
306
+ "ec2:DeleteVpcEndpoint",
307
+ "ec2:DescribeVpcEndpoints",
308
+ "ec2:ModifyVpcEndpoint",
309
+ "ec2:CreateNatGateway",
310
+ "ec2:DeleteNatGateway",
311
+ "ec2:DescribeNatGateways",
312
+ "ec2:AllocateAddress",
313
+ "ec2:ReleaseAddress",
314
+ "ec2:DescribeAddresses",
315
+ "ec2:CreateRouteTable",
316
+ "ec2:DeleteRouteTable",
317
+ "ec2:DescribeRouteTables",
318
+ "ec2:CreateRoute",
319
+ "ec2:DeleteRoute",
320
+ "ec2:AssociateRouteTable",
321
+ "ec2:DisassociateRouteTable",
322
+ "ec2:CreateSecurityGroup",
323
+ "ec2:DeleteSecurityGroup",
324
+ "ec2:AuthorizeSecurityGroupEgress",
325
+ "ec2:AuthorizeSecurityGroupIngress",
326
+ "ec2:RevokeSecurityGroupEgress",
327
+ "ec2:RevokeSecurityGroupIngress",
328
+ "ec2:DeleteSubnet",
329
+ "ec2:DetachInternetGateway"
330
+ ],
331
+ "Resource": "*",
332
+ "Condition": {
333
+ "StringLike": {
334
+ "ec2:CreateAction": [
335
+ "CreateVpcEndpoint",
336
+ "CreateNatGateway",
337
+ "CreateRouteTable",
338
+ "CreateRoute",
339
+ "CreateSecurityGroup"
340
+ ]
341
+ }
342
+ }
316
343
  }
317
- }
318
- }
319
- ]
344
+ ]
320
345
  }
321
346
  ```
322
347
 
323
348
  **What this enables:**
324
- - Creates NAT Gateway for Lambda internet access to external APIs (Salesforce, HubSpot, etc.)
325
- - Creates VPC endpoints for AWS services (S3, DynamoDB, KMS, SSM) to reduce NAT Gateway costs
326
- - Creates route tables and subnet associations for proper Lambda networking
327
- - Automatically configures your Lambda functions to run in your default VPC with full internet access
349
+
350
+ - Creates NAT Gateway for Lambda internet access to external APIs (Salesforce, HubSpot, etc.)
351
+ - Creates VPC endpoints for AWS services (S3, DynamoDB, KMS, SSM) to reduce NAT Gateway costs
352
+ - Creates route tables and subnet associations for proper Lambda networking
353
+ - Automatically configures your Lambda functions to run in your default VPC with full internet access
328
354
 
329
355
  ### KMS Support
330
356
 
@@ -332,35 +358,31 @@ Additional permissions needed when your app definition includes `encryption: { u
332
358
 
333
359
  ```json
334
360
  {
335
- "Version": "2012-10-17",
336
- "Statement": [
337
- {
338
- "Sid": "FriggKMSEncryptionRuntime",
339
- "Effect": "Allow",
340
- "Action": [
341
- "kms:GenerateDataKey",
342
- "kms:Decrypt"
343
- ],
344
- "Resource": [
345
- "arn:aws:kms:*:*:key/*"
346
- ],
347
- "Condition": {
348
- "StringEquals": {
349
- "kms:ViaService": [
350
- "lambda.*.amazonaws.com",
351
- "s3.*.amazonaws.com"
352
- ]
361
+ "Version": "2012-10-17",
362
+ "Statement": [
363
+ {
364
+ "Sid": "FriggKMSEncryptionRuntime",
365
+ "Effect": "Allow",
366
+ "Action": ["kms:GenerateDataKey", "kms:Decrypt"],
367
+ "Resource": ["arn:aws:kms:*:*:key/*"],
368
+ "Condition": {
369
+ "StringEquals": {
370
+ "kms:ViaService": [
371
+ "lambda.*.amazonaws.com",
372
+ "s3.*.amazonaws.com"
373
+ ]
374
+ }
375
+ }
353
376
  }
354
- }
355
- }
356
- ]
377
+ ]
357
378
  }
358
379
  ```
359
380
 
360
381
  **What this enables:**
361
- - Lambda functions can encrypt and decrypt data using your default KMS key
362
- - Automatic discovery and configuration of customer-managed KMS keys
363
- - Fallback to AWS-managed keys if no customer keys are available
382
+
383
+ - Lambda functions can encrypt and decrypt data using your default KMS key
384
+ - Automatic discovery and configuration of customer-managed KMS keys
385
+ - Fallback to AWS-managed keys if no customer keys are available
364
386
 
365
387
  ### SSM Parameter Store Support
366
388
 
@@ -368,29 +390,30 @@ Additional permissions needed when your app definition includes `ssm: { enable:
368
390
 
369
391
  ```json
370
392
  {
371
- "Version": "2012-10-17",
372
- "Statement": [
373
- {
374
- "Sid": "FriggSSMParameterAccess",
375
- "Effect": "Allow",
376
- "Action": [
377
- "ssm:GetParameter",
378
- "ssm:GetParameters",
379
- "ssm:GetParametersByPath"
380
- ],
381
- "Resource": [
382
- "arn:aws:ssm:*:*:parameter/*frigg*",
383
- "arn:aws:ssm:*:*:parameter/*frigg*/*"
384
- ]
385
- }
386
- ]
393
+ "Version": "2012-10-17",
394
+ "Statement": [
395
+ {
396
+ "Sid": "FriggSSMParameterAccess",
397
+ "Effect": "Allow",
398
+ "Action": [
399
+ "ssm:GetParameter",
400
+ "ssm:GetParameters",
401
+ "ssm:GetParametersByPath"
402
+ ],
403
+ "Resource": [
404
+ "arn:aws:ssm:*:*:parameter/*frigg*",
405
+ "arn:aws:ssm:*:*:parameter/*frigg*/*"
406
+ ]
407
+ }
408
+ ]
387
409
  }
388
410
  ```
389
411
 
390
412
  **What this enables:**
391
- - Lambda functions can retrieve configuration from SSM Parameter Store
392
- - Automatic configuration of AWS Parameters and Secrets Lambda Extension layer
393
- - Secure environment variable management through SSM
413
+
414
+ - Lambda functions can retrieve configuration from SSM Parameter Store
415
+ - Automatic configuration of AWS Parameters and Secrets Lambda Extension layer
416
+ - Secure environment variable management through SSM
394
417
 
395
418
  ## Complete Policy Template
396
419
 
@@ -398,63 +421,65 @@ For convenience, here's a single IAM policy that includes all permissions needed
398
421
 
399
422
  ```json
400
423
  {
401
- "Version": "2012-10-17",
402
- "Statement": [
403
- {
404
- "Sid": "FriggCorePermissions",
405
- "Effect": "Allow",
406
- "Action": [
407
- "sts:GetCallerIdentity",
408
- "cloudformation:*",
409
- "lambda:*",
410
- "apigateway:*",
411
- "logs:*",
412
- "sqs:*",
413
- "sns:*",
414
- "cloudwatch:*",
415
- "ec2:Describe*",
416
- "ec2:CreateVpcEndpoint",
417
- "ec2:DeleteVpcEndpoint",
418
- "ec2:ModifyVpcEndpoint",
419
- "kms:ListKeys",
420
- "kms:DescribeKey",
421
- "kms:GenerateDataKey",
422
- "kms:Decrypt",
423
- "ssm:GetParameter*"
424
- ],
425
- "Resource": "*"
426
- },
427
- {
428
- "Sid": "S3DeploymentBuckets",
429
- "Effect": "Allow",
430
- "Action": [
431
- "s3:*"
432
- ],
433
- "Resource": [
434
- "arn:aws:s3:::*serverless*",
435
- "arn:aws:s3:::*serverless*/*"
436
- ]
437
- },
438
- {
439
- "Sid": "IAMRoleManagement",
440
- "Effect": "Allow",
441
- "Action": [
442
- "iam:CreateRole",
443
- "iam:DeleteRole",
444
- "iam:GetRole",
445
- "iam:PassRole",
446
- "iam:PutRolePolicy",
447
- "iam:DeleteRolePolicy",
448
- "iam:GetRolePolicy",
449
- "iam:AttachRolePolicy",
450
- "iam:DetachRolePolicy",
451
- "iam:TagRole",
452
- "iam:UntagRole",
453
- "iam:ListPolicyVersions"
454
- ],
455
- "Resource": "arn:aws:iam::*:role/*"
456
- }
457
- ]
424
+ "Version": "2012-10-17",
425
+ "Statement": [
426
+ {
427
+ "Sid": "FriggCorePermissions",
428
+ "Effect": "Allow",
429
+ "Action": [
430
+ "sts:GetCallerIdentity",
431
+ "cloudformation:*",
432
+ "lambda:*",
433
+ "apigateway:GET",
434
+ "apigateway:POST",
435
+ "apigateway:PUT",
436
+ "apigateway:DELETE",
437
+ "apigateway:PATCH",
438
+ "logs:*",
439
+ "sqs:*",
440
+ "sns:*",
441
+ "cloudwatch:*",
442
+ "ec2:Describe*",
443
+ "ec2:CreateVpcEndpoint",
444
+ "ec2:DeleteVpcEndpoint",
445
+ "ec2:ModifyVpcEndpoint",
446
+ "kms:ListKeys",
447
+ "kms:DescribeKey",
448
+ "kms:GenerateDataKey",
449
+ "kms:Decrypt",
450
+ "ssm:GetParameter*"
451
+ ],
452
+ "Resource": "*"
453
+ },
454
+ {
455
+ "Sid": "S3DeploymentBuckets",
456
+ "Effect": "Allow",
457
+ "Action": ["s3:*"],
458
+ "Resource": [
459
+ "arn:aws:s3:::*serverless*",
460
+ "arn:aws:s3:::*serverless*/*"
461
+ ]
462
+ },
463
+ {
464
+ "Sid": "IAMRoleManagement",
465
+ "Effect": "Allow",
466
+ "Action": [
467
+ "iam:CreateRole",
468
+ "iam:DeleteRole",
469
+ "iam:GetRole",
470
+ "iam:PassRole",
471
+ "iam:PutRolePolicy",
472
+ "iam:DeleteRolePolicy",
473
+ "iam:GetRolePolicy",
474
+ "iam:AttachRolePolicy",
475
+ "iam:DetachRolePolicy",
476
+ "iam:TagRole",
477
+ "iam:UntagRole",
478
+ "iam:ListPolicyVersions"
479
+ ],
480
+ "Resource": "arn:aws:iam::*:role/*"
481
+ }
482
+ ]
458
483
  }
459
484
  ```
460
485
 
@@ -465,11 +490,13 @@ For convenience, here's a single IAM policy that includes all permissions needed
465
490
  This policy has been updated to follow the principle of least privilege by scoping permissions to Frigg-specific resources:
466
491
 
467
492
  **Before (Overly Broad):**
493
+
468
494
  ```json
469
495
  "Resource": "*" // ❌ Too permissive
470
496
  ```
471
497
 
472
498
  **After (Frigg-Specific):**
499
+
473
500
  ```json
474
501
  "Resource": [
475
502
  "arn:aws:lambda:*:*:function:*frigg*" // ✅ Only functions containing "frigg"
@@ -491,15 +518,17 @@ This policy has been updated to follow the principle of least privilege by scopi
491
518
  For these permissions to work properly, ensure your Frigg applications follow the naming convention of including "frigg" in resource names:
492
519
 
493
520
  ✅ **Good Examples:**
494
- - `my-frigg-app-dev` (CloudFormation stack)
495
- - `integration-frigg-service-auth` (Lambda function)
496
- - `customer-frigg-platform-prod-auth` (Lambda function)
497
- - `/my-frigg-app/prod/database-url` (SSM parameter)
498
- - `internal-error-queue-dev` (SQS queue - special pattern for error queues)
521
+
522
+ - `my-frigg-app-dev` (CloudFormation stack)
523
+ - `integration-frigg-service-auth` (Lambda function)
524
+ - `customer-frigg-platform-prod-auth` (Lambda function)
525
+ - `/my-frigg-app/prod/database-url` (SSM parameter)
526
+ - `internal-error-queue-dev` (SQS queue - special pattern for error queues)
499
527
 
500
528
  ❌ **Won't Match:**
501
- - `my-integration-app-dev` (no "frigg" in name)
502
- - `customer-platform-prod` (no "frigg" in name)
529
+
530
+ - `my-integration-app-dev` (no "frigg" in name)
531
+ - `customer-platform-prod` (no "frigg" in name)
503
532
 
504
533
  **Note:** The `internal-error-queue-*` pattern is specifically allowed for error handling queues.
505
534
 
@@ -519,10 +548,10 @@ You can further restrict permissions by:
519
548
 
520
549
  ```json
521
550
  {
522
- "Resource": [
523
- "arn:aws:cloudformation:us-east-1:YOUR-ACCOUNT-ID:stack/your-app-*/*",
524
- "arn:aws:lambda:us-east-1:YOUR-ACCOUNT-ID:function:your-app-*"
525
- ]
551
+ "Resource": [
552
+ "arn:aws:cloudformation:us-east-1:YOUR-ACCOUNT-ID:stack/your-app-*/*",
553
+ "arn:aws:lambda:us-east-1:YOUR-ACCOUNT-ID:function:your-app-*"
554
+ ]
526
555
  }
527
556
  ```
528
557
 
@@ -530,15 +559,15 @@ You can further restrict permissions by:
530
559
 
531
560
  The discovery process sets these environment variables during build:
532
561
 
533
- - `AWS_DISCOVERY_VPC_ID` - Your default VPC ID
534
- - `AWS_DISCOVERY_SECURITY_GROUP_ID` - Default security group ID
535
- - `AWS_DISCOVERY_SUBNET_ID_1` - First private subnet ID (for Lambda functions)
536
- - `AWS_DISCOVERY_SUBNET_ID_2` - Second private subnet ID (for Lambda functions, or same as first if only one exists)
537
- - `AWS_DISCOVERY_PUBLIC_SUBNET_ID` - Public subnet ID (for NAT Gateway placement)
538
- - `AWS_DISCOVERY_ROUTE_TABLE_ID` - Private route table ID for VPC endpoints
539
- - `AWS_DISCOVERY_KMS_KEY_ID` - Default KMS key ARN
540
- - `AWS_DISCOVERY_NAT_GATEWAY_ID` - Existing NAT Gateway ID (if found)
541
- - `AWS_DISCOVERY_ELASTIC_IP_ALLOCATION_ID` - Existing Elastic IP allocation ID (if found)
562
+ - `AWS_DISCOVERY_VPC_ID` - Your default VPC ID
563
+ - `AWS_DISCOVERY_SECURITY_GROUP_ID` - Default security group ID
564
+ - `AWS_DISCOVERY_SUBNET_ID_1` - First private subnet ID (for Lambda functions)
565
+ - `AWS_DISCOVERY_SUBNET_ID_2` - Second private subnet ID (for Lambda functions, or same as first if only one exists)
566
+ - `AWS_DISCOVERY_PUBLIC_SUBNET_ID` - Public subnet ID (for NAT Gateway placement)
567
+ - `AWS_DISCOVERY_ROUTE_TABLE_ID` - Private route table ID for VPC endpoints
568
+ - `AWS_DISCOVERY_KMS_KEY_ID` - Default KMS key ARN
569
+ - `AWS_DISCOVERY_NAT_GATEWAY_ID` - Existing NAT Gateway ID (if found)
570
+ - `AWS_DISCOVERY_ELASTIC_IP_ALLOCATION_ID` - Existing Elastic IP allocation ID (if found)
542
571
 
543
572
  ## Troubleshooting
544
573
 
@@ -557,17 +586,19 @@ The discovery process sets these environment variables during build:
557
586
  ### Fallback Behavior
558
587
 
559
588
  If AWS discovery fails during build, the framework will:
560
- - Log a warning message
561
- - Set fallback environment variables
562
- - Continue with deployment using safe default values
563
- - Not fail the build process
589
+
590
+ - Log a warning message
591
+ - Set fallback environment variables
592
+ - Continue with deployment using safe default values
593
+ - Not fail the build process
564
594
 
565
595
  ### Regional Considerations
566
596
 
567
597
  Ensure your IAM policy includes permissions for the AWS region where you're deploying:
568
- - Discovery permissions work across all regions (use `*` in resource ARNs)
569
- - Deployment permissions should match your target region
570
- - Some services like IAM are global, others are region-specific
598
+
599
+ - Discovery permissions work across all regions (use `*` in resource ARNs)
600
+ - Deployment permissions should match your target region
601
+ - Some services like IAM are global, others are region-specific
571
602
 
572
603
  ## Using with CI/CD
573
604
 
@@ -584,13 +615,13 @@ Example GitHub Actions configuration:
584
615
  - name: Configure AWS credentials
585
616
  uses: aws-actions/configure-aws-credentials@v1
586
617
  with:
587
- aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
588
- aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
589
- aws-region: us-east-1
618
+ aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
619
+ aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
620
+ aws-region: us-east-1
590
621
 
591
622
  - name: Deploy Frigg App
592
623
  run: |
593
- frigg deploy
624
+ frigg deploy
594
625
  ```
595
626
 
596
- This policy ensures your Frigg application can successfully discover AWS resources during build time and deploy all necessary infrastructure components during deployment.
627
+ This policy ensures your Frigg application can successfully discover AWS resources during build time and deploy all necessary infrastructure components during deployment.