@friggframework/devtools 2.0.0--canary.397.155fecd.0 → 2.0.0--canary.398.e2147f7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,7 +1,14 @@
1
1
  const path = require('path');
2
2
  const fs = require('fs');
3
3
 
4
- // Function to find the actual path to node_modules
4
+ /**
5
+ * Find the actual path to node_modules directory
6
+ * Tries multiple methods to locate node_modules:
7
+ * 1. Traversing up from current directory
8
+ * 2. Using npm root command
9
+ * 3. Looking for package.json and adjacent node_modules
10
+ * @returns {string} Path to node_modules directory
11
+ */
5
12
  const findNodeModulesPath = () => {
6
13
  try {
7
14
  // Method 1: Try to find node_modules by traversing up from current directory
@@ -75,7 +82,12 @@ const findNodeModulesPath = () => {
75
82
  }
76
83
  };
77
84
 
78
- // Function to modify handler paths to point to the correct node_modules
85
+ /**
86
+ * Modify handler paths to point to the correct node_modules location
87
+ * Only modifies paths when running in offline mode
88
+ * @param {Object} functions - Serverless functions configuration object
89
+ * @returns {Object} Modified functions object with updated handler paths
90
+ */
79
91
  const modifyHandlerPaths = (functions) => {
80
92
  // Check if we're running in offline mode
81
93
  const isOffline = process.argv.includes('offline');
@@ -94,7 +106,8 @@ const modifyHandlerPaths = (functions) => {
94
106
  const functionDef = modifiedFunctions[functionName];
95
107
  if (functionDef?.handler?.includes('node_modules/')) {
96
108
  // Replace node_modules/ with the actual path to node_modules/
97
- functionDef.handler = functionDef.handler.replace('node_modules/', '../node_modules/');
109
+ const relativePath = path.relative(process.cwd(), nodeModulesPath);
110
+ functionDef.handler = functionDef.handler.replace('node_modules/', `${relativePath}/`);
98
111
  console.log(`Updated handler for ${functionName}: ${functionDef.handler}`);
99
112
  }
100
113
  }
@@ -102,6 +115,327 @@ const modifyHandlerPaths = (functions) => {
102
115
  return modifiedFunctions;
103
116
  };
104
117
 
118
+ /**
119
+ * Create VPC infrastructure resources for CloudFormation
120
+ * Creates VPC, subnets, NAT gateway, route tables, and security groups
121
+ * @param {Object} AppDefinition - Application definition object
122
+ * @param {Object} AppDefinition.vpc - VPC configuration
123
+ * @param {string} [AppDefinition.vpc.cidrBlock='10.0.0.0/16'] - CIDR block for VPC
124
+ * @returns {Object} CloudFormation resources for VPC infrastructure
125
+ */
126
+ const createVPCInfrastructure = (AppDefinition) => {
127
+ const vpcResources = {
128
+ // VPC
129
+ FriggVPC: {
130
+ Type: 'AWS::EC2::VPC',
131
+ Properties: {
132
+ CidrBlock: AppDefinition.vpc.cidrBlock || '10.0.0.0/16',
133
+ EnableDnsHostnames: true,
134
+ EnableDnsSupport: true,
135
+ Tags: [
136
+ { Key: 'Name', Value: '${self:service}-${self:provider.stage}-vpc' }
137
+ ]
138
+ }
139
+ },
140
+
141
+ // Internet Gateway
142
+ FriggInternetGateway: {
143
+ Type: 'AWS::EC2::InternetGateway',
144
+ Properties: {
145
+ Tags: [
146
+ { Key: 'Name', Value: '${self:service}-${self:provider.stage}-igw' }
147
+ ]
148
+ }
149
+ },
150
+
151
+ // Attach Internet Gateway to VPC
152
+ FriggVPCGatewayAttachment: {
153
+ Type: 'AWS::EC2::VPCGatewayAttachment',
154
+ Properties: {
155
+ VpcId: { Ref: 'FriggVPC' },
156
+ InternetGatewayId: { Ref: 'FriggInternetGateway' }
157
+ }
158
+ },
159
+
160
+ // Public Subnet for NAT Gateway
161
+ FriggPublicSubnet: {
162
+ Type: 'AWS::EC2::Subnet',
163
+ Properties: {
164
+ VpcId: { Ref: 'FriggVPC' },
165
+ CidrBlock: '10.0.1.0/24',
166
+ AvailabilityZone: { 'Fn::Select': [0, { 'Fn::GetAZs': '' }] },
167
+ MapPublicIpOnLaunch: true,
168
+ Tags: [
169
+ { Key: 'Name', Value: '${self:service}-${self:provider.stage}-public-subnet' }
170
+ ]
171
+ }
172
+ },
173
+
174
+ // Private Subnet 1 for Lambda
175
+ FriggPrivateSubnet1: {
176
+ Type: 'AWS::EC2::Subnet',
177
+ Properties: {
178
+ VpcId: { Ref: 'FriggVPC' },
179
+ CidrBlock: '10.0.2.0/24',
180
+ AvailabilityZone: { 'Fn::Select': [0, { 'Fn::GetAZs': '' }] },
181
+ Tags: [
182
+ { Key: 'Name', Value: '${self:service}-${self:provider.stage}-private-subnet-1' }
183
+ ]
184
+ }
185
+ },
186
+
187
+ // Private Subnet 2 for Lambda (different AZ for redundancy)
188
+ FriggPrivateSubnet2: {
189
+ Type: 'AWS::EC2::Subnet',
190
+ Properties: {
191
+ VpcId: { Ref: 'FriggVPC' },
192
+ CidrBlock: '10.0.3.0/24',
193
+ AvailabilityZone: { 'Fn::Select': [1, { 'Fn::GetAZs': '' }] },
194
+ Tags: [
195
+ { Key: 'Name', Value: '${self:service}-${self:provider.stage}-private-subnet-2' }
196
+ ]
197
+ }
198
+ },
199
+
200
+ // Elastic IP for NAT Gateway
201
+ FriggNATGatewayEIP: {
202
+ Type: 'AWS::EC2::EIP',
203
+ Properties: {
204
+ Domain: 'vpc',
205
+ Tags: [
206
+ { Key: 'Name', Value: '${self:service}-${self:provider.stage}-nat-eip' }
207
+ ]
208
+ },
209
+ DependsOn: 'FriggVPCGatewayAttachment'
210
+ },
211
+
212
+ // NAT Gateway for private subnet internet access
213
+ FriggNATGateway: {
214
+ Type: 'AWS::EC2::NatGateway',
215
+ Properties: {
216
+ AllocationId: { 'Fn::GetAtt': ['FriggNATGatewayEIP', 'AllocationId'] },
217
+ SubnetId: { Ref: 'FriggPublicSubnet' },
218
+ Tags: [
219
+ { Key: 'Name', Value: '${self:service}-${self:provider.stage}-nat-gateway' }
220
+ ]
221
+ }
222
+ },
223
+
224
+ // Public Route Table
225
+ FriggPublicRouteTable: {
226
+ Type: 'AWS::EC2::RouteTable',
227
+ Properties: {
228
+ VpcId: { Ref: 'FriggVPC' },
229
+ Tags: [
230
+ { Key: 'Name', Value: '${self:service}-${self:provider.stage}-public-rt' }
231
+ ]
232
+ }
233
+ },
234
+
235
+ // Public Route to Internet Gateway
236
+ FriggPublicRoute: {
237
+ Type: 'AWS::EC2::Route',
238
+ Properties: {
239
+ RouteTableId: { Ref: 'FriggPublicRouteTable' },
240
+ DestinationCidrBlock: '0.0.0.0/0',
241
+ GatewayId: { Ref: 'FriggInternetGateway' }
242
+ },
243
+ DependsOn: 'FriggVPCGatewayAttachment'
244
+ },
245
+
246
+ // Associate Public Subnet with Public Route Table
247
+ FriggPublicSubnetRouteTableAssociation: {
248
+ Type: 'AWS::EC2::SubnetRouteTableAssociation',
249
+ Properties: {
250
+ SubnetId: { Ref: 'FriggPublicSubnet' },
251
+ RouteTableId: { Ref: 'FriggPublicRouteTable' }
252
+ }
253
+ },
254
+
255
+ // Private Route Table
256
+ FriggPrivateRouteTable: {
257
+ Type: 'AWS::EC2::RouteTable',
258
+ Properties: {
259
+ VpcId: { Ref: 'FriggVPC' },
260
+ Tags: [
261
+ { Key: 'Name', Value: '${self:service}-${self:provider.stage}-private-rt' }
262
+ ]
263
+ }
264
+ },
265
+
266
+ // Private Route to NAT Gateway
267
+ FriggPrivateRoute: {
268
+ Type: 'AWS::EC2::Route',
269
+ Properties: {
270
+ RouteTableId: { Ref: 'FriggPrivateRouteTable' },
271
+ DestinationCidrBlock: '0.0.0.0/0',
272
+ NatGatewayId: { Ref: 'FriggNATGateway' }
273
+ }
274
+ },
275
+
276
+ // Associate Private Subnet 1 with Private Route Table
277
+ FriggPrivateSubnet1RouteTableAssociation: {
278
+ Type: 'AWS::EC2::SubnetRouteTableAssociation',
279
+ Properties: {
280
+ SubnetId: { Ref: 'FriggPrivateSubnet1' },
281
+ RouteTableId: { Ref: 'FriggPrivateRouteTable' }
282
+ }
283
+ },
284
+
285
+ // Associate Private Subnet 2 with Private Route Table
286
+ FriggPrivateSubnet2RouteTableAssociation: {
287
+ Type: 'AWS::EC2::SubnetRouteTableAssociation',
288
+ Properties: {
289
+ SubnetId: { Ref: 'FriggPrivateSubnet2' },
290
+ RouteTableId: { Ref: 'FriggPrivateRouteTable' }
291
+ }
292
+ },
293
+
294
+ // Security Group for Lambda functions
295
+ FriggLambdaSecurityGroup: {
296
+ Type: 'AWS::EC2::SecurityGroup',
297
+ Properties: {
298
+ GroupDescription: 'Security group for Frigg Lambda functions',
299
+ VpcId: { Ref: 'FriggVPC' },
300
+ SecurityGroupEgress: [
301
+ {
302
+ IpProtocol: 'tcp',
303
+ FromPort: 443,
304
+ ToPort: 443,
305
+ CidrIp: '0.0.0.0/0',
306
+ Description: 'HTTPS outbound'
307
+ },
308
+ {
309
+ IpProtocol: 'tcp',
310
+ FromPort: 80,
311
+ ToPort: 80,
312
+ CidrIp: '0.0.0.0/0',
313
+ Description: 'HTTP outbound'
314
+ },
315
+ {
316
+ IpProtocol: 'tcp',
317
+ FromPort: 53,
318
+ ToPort: 53,
319
+ CidrIp: '0.0.0.0/0',
320
+ Description: 'DNS TCP'
321
+ },
322
+ {
323
+ IpProtocol: 'udp',
324
+ FromPort: 53,
325
+ ToPort: 53,
326
+ CidrIp: '0.0.0.0/0',
327
+ Description: 'DNS UDP'
328
+ }
329
+ ],
330
+ Tags: [
331
+ { Key: 'Name', Value: '${self:service}-${self:provider.stage}-lambda-sg' }
332
+ ]
333
+ }
334
+ }
335
+ };
336
+
337
+ // Add VPC Endpoints for cost optimization
338
+ if (AppDefinition.vpc.enableVPCEndpoints !== false) {
339
+ // S3 Gateway Endpoint (free)
340
+ vpcResources.FriggS3VPCEndpoint = {
341
+ Type: 'AWS::EC2::VPCEndpoint',
342
+ Properties: {
343
+ VpcId: { Ref: 'FriggVPC' },
344
+ ServiceName: 'com.amazonaws.${self:provider.region}.s3',
345
+ VpcEndpointType: 'Gateway',
346
+ RouteTableIds: [
347
+ { Ref: 'FriggPrivateRouteTable' }
348
+ ]
349
+ }
350
+ };
351
+
352
+ // DynamoDB Gateway Endpoint (free)
353
+ vpcResources.FriggDynamoDBVPCEndpoint = {
354
+ Type: 'AWS::EC2::VPCEndpoint',
355
+ Properties: {
356
+ VpcId: { Ref: 'FriggVPC' },
357
+ ServiceName: 'com.amazonaws.${self:provider.region}.dynamodb',
358
+ VpcEndpointType: 'Gateway',
359
+ RouteTableIds: [
360
+ { Ref: 'FriggPrivateRouteTable' }
361
+ ]
362
+ }
363
+ };
364
+
365
+ // KMS Interface Endpoint (paid, but useful if using KMS)
366
+ if (AppDefinition.encryption?.useDefaultKMSForFieldLevelEncryption === true) {
367
+ vpcResources.FriggKMSVPCEndpoint = {
368
+ Type: 'AWS::EC2::VPCEndpoint',
369
+ Properties: {
370
+ VpcId: { Ref: 'FriggVPC' },
371
+ ServiceName: 'com.amazonaws.${self:provider.region}.kms',
372
+ VpcEndpointType: 'Interface',
373
+ SubnetIds: [
374
+ { Ref: 'FriggPrivateSubnet1' },
375
+ { Ref: 'FriggPrivateSubnet2' }
376
+ ],
377
+ SecurityGroupIds: [
378
+ { Ref: 'FriggVPCEndpointSecurityGroup' }
379
+ ],
380
+ PrivateDnsEnabled: true
381
+ }
382
+ };
383
+ }
384
+
385
+ // Secrets Manager Interface Endpoint (paid, but useful for secrets)
386
+ vpcResources.FriggSecretsManagerVPCEndpoint = {
387
+ Type: 'AWS::EC2::VPCEndpoint',
388
+ Properties: {
389
+ VpcId: { Ref: 'FriggVPC' },
390
+ ServiceName: 'com.amazonaws.${self:provider.region}.secretsmanager',
391
+ VpcEndpointType: 'Interface',
392
+ SubnetIds: [
393
+ { Ref: 'FriggPrivateSubnet1' },
394
+ { Ref: 'FriggPrivateSubnet2' }
395
+ ],
396
+ SecurityGroupIds: [
397
+ { Ref: 'FriggVPCEndpointSecurityGroup' }
398
+ ],
399
+ PrivateDnsEnabled: true
400
+ }
401
+ };
402
+
403
+ // Security Group for VPC Endpoints
404
+ vpcResources.FriggVPCEndpointSecurityGroup = {
405
+ Type: 'AWS::EC2::SecurityGroup',
406
+ Properties: {
407
+ GroupDescription: 'Security group for Frigg VPC Endpoints',
408
+ VpcId: { Ref: 'FriggVPC' },
409
+ SecurityGroupIngress: [
410
+ {
411
+ IpProtocol: 'tcp',
412
+ FromPort: 443,
413
+ ToPort: 443,
414
+ SourceSecurityGroupId: { Ref: 'FriggLambdaSecurityGroup' },
415
+ Description: 'HTTPS from Lambda'
416
+ }
417
+ ],
418
+ Tags: [
419
+ { Key: 'Name', Value: '${self:service}-${self:provider.stage}-vpc-endpoint-sg' }
420
+ ]
421
+ }
422
+ };
423
+ }
424
+
425
+ return vpcResources;
426
+ };
427
+
428
+ /**
429
+ * Compose a complete serverless framework configuration from app definition
430
+ * @param {Object} AppDefinition - Application definition object
431
+ * @param {string} [AppDefinition.name] - Application name
432
+ * @param {string} [AppDefinition.provider='aws'] - Cloud provider
433
+ * @param {Array} AppDefinition.integrations - Array of integration definitions
434
+ * @param {Object} [AppDefinition.vpc] - VPC configuration
435
+ * @param {Object} [AppDefinition.encryption] - KMS encryption configuration
436
+ * @param {Object} [AppDefinition.ssm] - SSM parameter store configuration
437
+ * @returns {Object} Complete serverless framework configuration
438
+ */
105
439
  const composeServerlessDefinition = (AppDefinition) => {
106
440
  const definition = {
107
441
  frameworkVersion: '>=3.17.0',
@@ -245,7 +579,7 @@ const composeServerlessDefinition = (AppDefinition) => {
245
579
  Type: 'AWS::SQS::Queue',
246
580
  Properties: {
247
581
  QueueName:
248
- 'internal-error-queue-${self:provider.stage}',
582
+ '${self:service}-internal-error-queue-${self:provider.stage}',
249
583
  MessageRetentionPeriod: 300,
250
584
  },
251
585
  },
@@ -329,6 +663,7 @@ const composeServerlessDefinition = (AppDefinition) => {
329
663
  },
330
664
  };
331
665
 
666
+
332
667
  // KMS Configuration based on App Definition
333
668
  if (AppDefinition.encryption?.useDefaultKMSForFieldLevelEncryption === true) {
334
669
  // Add KMS IAM permissions
@@ -347,12 +682,181 @@ const composeServerlessDefinition = (AppDefinition) => {
347
682
  // Add serverless-kms-grants plugin
348
683
  definition.plugins.push('serverless-kms-grants');
349
684
 
350
- // Configure KMS grants with default key
685
+ // Configure KMS grants with discovered default key
351
686
  definition.custom.kmsGrants = {
352
- kmsKeyId: '*'
687
+ kmsKeyId: '${env:AWS_DISCOVERY_KMS_KEY_ID}'
353
688
  };
354
689
  }
355
690
 
691
+ // VPC Configuration based on App Definition
692
+ if (AppDefinition.vpc?.enable === true) {
693
+ // Add VPC-related IAM permissions
694
+ definition.provider.iamRoleStatements.push({
695
+ Effect: 'Allow',
696
+ Action: [
697
+ 'ec2:CreateNetworkInterface',
698
+ 'ec2:DescribeNetworkInterfaces',
699
+ 'ec2:DeleteNetworkInterface',
700
+ 'ec2:AttachNetworkInterface',
701
+ 'ec2:DetachNetworkInterface'
702
+ ],
703
+ Resource: '*'
704
+ });
705
+
706
+ // Default approach: Use AWS Discovery to find existing VPC resources
707
+ if (AppDefinition.vpc.createNew === true) {
708
+ // Option 1: Create new VPC infrastructure (explicit opt-in)
709
+ const vpcConfig = {};
710
+
711
+ if (AppDefinition.vpc.securityGroupIds) {
712
+ // User provided custom security groups
713
+ vpcConfig.securityGroupIds = AppDefinition.vpc.securityGroupIds;
714
+ } else {
715
+ // Use auto-created security group
716
+ vpcConfig.securityGroupIds = [{ Ref: 'FriggLambdaSecurityGroup' }];
717
+ }
718
+
719
+ if (AppDefinition.vpc.subnetIds) {
720
+ // User provided custom subnets
721
+ vpcConfig.subnetIds = AppDefinition.vpc.subnetIds;
722
+ } else {
723
+ // Use auto-created private subnets
724
+ vpcConfig.subnetIds = [
725
+ { Ref: 'FriggPrivateSubnet1' },
726
+ { Ref: 'FriggPrivateSubnet2' }
727
+ ];
728
+ }
729
+
730
+ // Set VPC config for Lambda functions
731
+ definition.provider.vpc = vpcConfig;
732
+
733
+ // Add VPC infrastructure resources to CloudFormation
734
+ const vpcResources = createVPCInfrastructure(AppDefinition);
735
+ Object.assign(definition.resources.Resources, vpcResources);
736
+ } else {
737
+ // Option 2: Use AWS Discovery (default behavior)
738
+ // VPC configuration using discovered or explicitly provided resources
739
+ const vpcConfig = {
740
+ securityGroupIds: AppDefinition.vpc.securityGroupIds ||
741
+ ['${env:AWS_DISCOVERY_SECURITY_GROUP_ID}'],
742
+ subnetIds: AppDefinition.vpc.subnetIds || [
743
+ '${env:AWS_DISCOVERY_SUBNET_ID_1}',
744
+ '${env:AWS_DISCOVERY_SUBNET_ID_2}'
745
+ ]
746
+ };
747
+
748
+ // Set VPC config for Lambda functions
749
+ definition.provider.vpc = vpcConfig;
750
+
751
+ // Add NAT Gateway for Lambda internet access (required for external API calls)
752
+ // Even with existing VPC, Lambdas need guaranteed internet access for integrations
753
+ definition.resources.Resources.FriggNATGatewayEIP = {
754
+ Type: 'AWS::EC2::EIP',
755
+ Properties: {
756
+ Domain: 'vpc',
757
+ Tags: [
758
+ { Key: 'Name', Value: '${self:service}-${self:provider.stage}-nat-eip' }
759
+ ]
760
+ }
761
+ };
762
+
763
+ definition.resources.Resources.FriggNATGateway = {
764
+ Type: 'AWS::EC2::NatGateway',
765
+ Properties: {
766
+ AllocationId: { 'Fn::GetAtt': ['FriggNATGatewayEIP', 'AllocationId'] },
767
+ SubnetId: '${env:AWS_DISCOVERY_PUBLIC_SUBNET_ID}', // Discovery finds public subnet
768
+ Tags: [
769
+ { Key: 'Name', Value: '${self:service}-${self:provider.stage}-nat-gateway' }
770
+ ]
771
+ }
772
+ };
773
+
774
+ // Create route table for Lambda subnets to use NAT Gateway
775
+ definition.resources.Resources.FriggLambdaRouteTable = {
776
+ Type: 'AWS::EC2::RouteTable',
777
+ Properties: {
778
+ VpcId: '${env:AWS_DISCOVERY_VPC_ID}',
779
+ Tags: [
780
+ { Key: 'Name', Value: '${self:service}-${self:provider.stage}-lambda-rt' }
781
+ ]
782
+ }
783
+ };
784
+
785
+ definition.resources.Resources.FriggNATRoute = {
786
+ Type: 'AWS::EC2::Route',
787
+ Properties: {
788
+ RouteTableId: { Ref: 'FriggLambdaRouteTable' },
789
+ DestinationCidrBlock: '0.0.0.0/0',
790
+ NatGatewayId: { Ref: 'FriggNATGateway' }
791
+ }
792
+ };
793
+
794
+ // Associate Lambda subnets with NAT Gateway route table
795
+ definition.resources.Resources.FriggSubnet1RouteAssociation = {
796
+ Type: 'AWS::EC2::SubnetRouteTableAssociation',
797
+ Properties: {
798
+ SubnetId: '${env:AWS_DISCOVERY_SUBNET_ID_1}',
799
+ RouteTableId: { Ref: 'FriggLambdaRouteTable' }
800
+ }
801
+ };
802
+
803
+ definition.resources.Resources.FriggSubnet2RouteAssociation = {
804
+ Type: 'AWS::EC2::SubnetRouteTableAssociation',
805
+ Properties: {
806
+ SubnetId: '${env:AWS_DISCOVERY_SUBNET_ID_2}',
807
+ RouteTableId: { Ref: 'FriggLambdaRouteTable' }
808
+ }
809
+ };
810
+
811
+ // Add VPC endpoints for AWS service optimization (optional but recommended)
812
+ if (AppDefinition.vpc.enableVPCEndpoints !== false) {
813
+ definition.resources.Resources.VPCEndpointS3 = {
814
+ Type: 'AWS::EC2::VPCEndpoint',
815
+ Properties: {
816
+ VpcId: '${env:AWS_DISCOVERY_VPC_ID}',
817
+ ServiceName: 'com.amazonaws.${self:provider.region}.s3',
818
+ VpcEndpointType: 'Gateway',
819
+ RouteTableIds: [{ Ref: 'FriggLambdaRouteTable' }]
820
+ }
821
+ };
822
+
823
+ definition.resources.Resources.VPCEndpointDynamoDB = {
824
+ Type: 'AWS::EC2::VPCEndpoint',
825
+ Properties: {
826
+ VpcId: '${env:AWS_DISCOVERY_VPC_ID}',
827
+ ServiceName: 'com.amazonaws.${self:provider.region}.dynamodb',
828
+ VpcEndpointType: 'Gateway',
829
+ RouteTableIds: [{ Ref: 'FriggLambdaRouteTable' }]
830
+ }
831
+ };
832
+ }
833
+ }
834
+ }
835
+
836
+ // SSM Parameter Store Configuration based on App Definition
837
+ if (AppDefinition.ssm?.enable === true) {
838
+ // Add AWS Parameters and Secrets Lambda Extension layer
839
+ definition.provider.layers = [
840
+ 'arn:aws:lambda:${self:provider.region}:177933569100:layer:AWS-Parameters-and-Secrets-Lambda-Extension:11'
841
+ ];
842
+
843
+ // Add SSM IAM permissions
844
+ definition.provider.iamRoleStatements.push({
845
+ Effect: 'Allow',
846
+ Action: [
847
+ 'ssm:GetParameter',
848
+ 'ssm:GetParameters',
849
+ 'ssm:GetParametersByPath'
850
+ ],
851
+ Resource: [
852
+ 'arn:aws:ssm:${self:provider.region}:*:parameter/${self:service}/${self:provider.stage}/*'
853
+ ]
854
+ });
855
+
856
+ // Add environment variable for SSM parameter prefix
857
+ definition.provider.environment.SSM_PARAMETER_PREFIX = '/${self:service}/${self:provider.stage}';
858
+ }
859
+
356
860
  // Add integration-specific functions and resources
357
861
  for (const integration of AppDefinition.integrations) {
358
862
  const integrationName = integration.Definition.name;