UNPKG

13.6 kBJavaScriptView Raw
1// builds cfn resources for lambda function
2const cf = require('@mapbox/cloudfriend');
3
4module.exports.build = build;
5function build(params) {
6 const functions = new Set([
7 'SnsSubscription',
8 'DynamoDBStreamLabel',
9 'StackOutputs',
10 'SpotFleet',
11 'DefaultVpc',
12 'S3NotificationTopicConfig',
13 'S3Inventory'
14 ]);
15
16 if(!params.CustomResourceName)
17 throw new Error('Missing CustomResourceName');
18 if (!functions.has(params.CustomResourceName))
19 throw new Error(`${params.CustomResourceName} is not an available magical-cfn-resource`);
20 if (!params.LogicalName)
21 throw new Error('Missing LogicalName');
22 if (!params.S3Bucket)
23 throw new Error('Missing S3Bucket');
24 if (!params.S3Key)
25 throw new Error('Missing S3Key');
26 if (!params.Handler)
27 throw new Error('Missing Handler');
28 if (!params.Properties)
29 throw new Error('Missing Properties');
30 if (params.CustomResourceName === 'SpotFleet') {
31 if (!params.Properties.SpotFleetRequestConfigData)
32 throw new Error('Missing SpotFleetRequestConfigData');
33 if (!params.Properties.SpotFleetRequestConfigData.LaunchSpecifications)
34 throw new Error('Missing LaunchSpecifications in SpotFleetRequestConfigData');
35 if (!params.Properties.SpotFleetRequestConfigData.LaunchSpecifications[0])
36 throw new Error('Missing LaunchSpecifications[0] in SpotFleetRequestConfigData');
37 if (!params.Properties.SpotFleetRequestConfigData.LaunchSpecifications[0].IamInstanceProfile)
38 throw new Error('Missing IamInstanceProfile in SpotFleetRequestConfigData.LaunchSpecifications[0]');
39 if (!params.Properties.SpotFleetRequestConfigData.IamFleetRole)
40 throw new Error('Missing IamFleetRole in SpotFleetRequestConfigData');
41 }
42
43 const CustomResource = {};
44
45 CustomResource[`${params.LogicalName}Role`] = role(params);
46
47 // build any additional resources
48 moreResources(params, CustomResource);
49
50 CustomResource[`${params.LogicalName}Function`] = {
51 Type: 'AWS::Lambda::Function',
52 // #### Properties
53 Properties: {
54 // - Code: You must upload your Lambda function as a .zip file
55 // to S3, and refer to it here.
56 Code: {
57 S3Bucket: params.S3Bucket,
58 S3Key: params.S3Key
59 },
60 // - Role: Refers to the ARN of the Role defined above.
61 Role: cf.getAtt(`${params.LogicalName}Role`, 'Arn'),
62 // - Other parameters as described by
63 // [the AWS documentation](http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-lambda-function.html).
64 Description: `Manages ${params.CustomResourceName}`,
65 Handler: params.Handler,
66 MemorySize: 128,
67 Runtime: 'nodejs16.x',
68 Timeout: 30
69 }
70 };
71 CustomResource[params.LogicalName] = {
72 Type: 'Custom::'+params.CustomResourceName,
73 Properties: Object.assign({ ServiceToken: cf.getAtt(`${params.LogicalName}Function`, 'Arn')}, params.Properties)
74 };
75
76 if (params.Condition) {
77 CustomResource[`${params.LogicalName}Role`]['Condition'] = params.Condition;
78 CustomResource[`${params.LogicalName}Function`]['Condition'] = params.Condition;
79 CustomResource[params.LogicalName]['Condition'] = params.Condition;
80 }
81
82 return { Resources: CustomResource };
83}
84
85function role(params) {
86 switch (params.CustomResourceName) {
87 case 'SnsSubscription':
88 return {
89 Type: 'AWS::IAM::Role',
90 Properties: {
91 // #### Assume role policy
92 // Identifies that this role can be assumed by a Lambda function.
93 AssumeRolePolicyDocument: {
94 Statement: [
95 {
96 Sid: '',
97 Effect: 'Allow',
98 Principal: {
99 Service: 'lambda.amazonaws.com'
100 },
101 Action: 'sts:AssumeRole'
102 }
103 ]
104 },
105 Policies: [
106 {
107 // #### Runtime policy
108 // Defines the permissions that the Lambda function will
109 // have once it has assumed this role.
110 PolicyName: 'SnsSubscriptionPolicy',
111 PolicyDocument: {
112 Statement: [
113 // The function can write CloudWatch Logs
114 {
115 Effect: 'Allow',
116 Action: ['logs:*'],
117 Resource: cf.sub('arn:${AWS::Partition}:logs:*:*:*')
118 },
119 // The function is able to create and delete subscriptions
120 // for all SNS topics.
121 {
122 Effect: 'Allow',
123 Action: [
124 'sns:Subscribe',
125 'sns:Unsubscribe',
126 'sns:ListSubscriptionsByTopic'
127 ],
128 Resource: '*'
129 }
130 ]
131 }
132 }
133 ]
134 }
135 };
136 case 'DynamoDBStreamLabel':
137 return {
138 Type: 'AWS::IAM::Role',
139 Properties: {
140 // #### Assume role policy
141 // Identifies that this role can be assumed by a Lambda function.
142 AssumeRolePolicyDocument: {
143 Statement: [
144 {
145 Sid: '',
146 Effect: 'Allow',
147 Principal: { Service: 'lambda.amazonaws.com' },
148 Action: 'sts:AssumeRole'
149 }
150 ]
151 },
152 Policies: [
153 {
154 // #### Runtime policy
155 // Defines the permissions that the Lambda function will have once
156 // it has assumed this role.
157 PolicyName: 'DynamoDBStreamLabelPolicy',
158 PolicyDocument: {
159 Statement: [
160 // The function can write CloudWatch Logs
161 {
162 Effect: 'Allow',
163 Action: ['logs:*'],
164 Resource: cf.sub('arn:${AWS::Partition}:logs:*:*:*')
165 },
166 // The function is able to describe DynamoDB tables
167 {
168 Effect: 'Allow',
169 Action: [
170 'dynamodb:DescribeTable'
171 ],
172 Resource: '*'
173 }
174 ]
175 }
176 }
177 ]
178 }
179 };
180 case 'StackOutputs':
181 return {
182 Type: 'AWS::IAM::Role',
183 Properties: {
184 // #### Assume role policy
185 // Identifies that this role can be assumed by a Lambda function.
186 AssumeRolePolicyDocument: {
187 Statement: [
188 {
189 Sid: '',
190 Effect: 'Allow',
191 Principal: {
192 Service: 'lambda.amazonaws.com'
193 },
194 Action: 'sts:AssumeRole'
195 }
196 ]
197 },
198 Policies: [
199 {
200 // #### Runtime policy
201 // Defines the permissions that the Lambda function will
202 // have once it has assumed this role.
203 PolicyName: 'LogGroupPolicy',
204 PolicyDocument: {
205 Statement: [
206 // The function can write to and create CloudWatch Logs
207 {
208 Effect: 'Allow',
209 Action: ['logs:*'],
210 Resource: cf.sub('arn:${AWS::Partition}:logs:*:*:*')
211 },
212 // The function can describe stacks
213 {
214 Effect: 'Allow',
215 Action: ['cloudformation:DescribeStacks'],
216 Resource: '*'
217 }
218 ]
219 }
220 }
221 ]
222 }
223
224 };
225 case 'S3NotificationTopicConfig':
226 return {
227 Type: 'AWS::IAM::Role',
228 Properties: {
229 // #### Assume role policy
230 // Identifies that this role can be assumed by a Lambda function.
231 AssumeRolePolicyDocument: {
232 Statement: [
233 {
234 Sid: '',
235 Effect: 'Allow',
236 Principal: {
237 Service: 'lambda.amazonaws.com'
238 },
239 Action: 'sts:AssumeRole'
240 }
241 ]
242 },
243 Policies: [
244 {
245 // #### Runtime policy
246 // Defines the permissions that the Lambda function will
247 // have once it has assumed this role.
248 PolicyName: 'S3NotificationTopicConfigPolicy',
249 PolicyDocument: {
250 Statement: [
251 // The function can write CloudWatch Logs
252 {
253 Effect: 'Allow',
254 Action: ['logs:*'],
255 Resource: cf.sub('arn:${AWS::Partition}:logs:*:*:*')
256 },
257 // it can getBucketNotification and putBucketNotification on any s3 bucket
258 {
259 Effect: 'Allow',
260 Action: ['s3:getBucketNotification', 's3:putBucketNotification'],
261 Resource: params.Properties.BucketNotificationResources || '*'
262 }
263 ]
264 }
265 }
266 ]
267 }
268 }
269 case 'SpotFleet':
270 return {
271 Type: 'AWS::IAM::Role',
272 Properties: {
273 // #### Assume role policy
274 // Identifies that this role can be assumed by a Lambda function.
275 AssumeRolePolicyDocument: {
276 Statement: [
277 {
278 Sid: '',
279 Effect: 'Allow',
280 Principal: {
281 Service: 'lambda.amazonaws.com'
282 },
283 Action: 'sts:AssumeRole'
284 }
285 ]
286 },
287 Policies: [
288 {
289 // #### Runtime policy
290 // Defines the permissions that the Lambda function will
291 // have once it has assumed this role.
292 PolicyName: 'SpotFleetPolicy',
293 PolicyDocument: {
294 Statement: [
295 // The function can write CloudWatch Logs
296 {
297 Effect: 'Allow',
298 Action: ['logs:*'],
299 Resource: cf.sub('arn:${AWS::Partition}:logs:*:*:*')
300 },
301 // The function is able to create and delete spot fleet requests
302 {
303 Effect: 'Allow',
304 Action: ['ec2:*'],
305 Resource: '*'
306 },
307 {
308 Effect: 'Allow',
309 Action: [
310 'iam:ListRoles',
311 'iam:PassRole'
312 ],
313 Resource: params.Properties.SpotFleetRequestConfigData.IamFleetRole
314 },
315 {
316 Effect: 'Allow',
317 Action: ['iam:ListInstanceProfiles'],
318 Resource: params.Properties.SpotFleetRequestConfigData.LaunchSpecifications[0].IamInstanceProfile.Arn
319 }
320 ]
321 }
322 }
323 ]
324 }
325 };
326 case 'DefaultVpc':
327 return {
328 Type: 'AWS::IAM::Role',
329 Properties: {
330 AssumeRolePolicyDocument: {
331 Statement: [
332 {
333 Sid: '',
334 Effect: 'Allow',
335 Principal: { Service: 'lambda.amazonaws.com' },
336 Action: 'sts:AssumeRole'
337 }
338 ]
339 },
340 Policies: [
341 {
342 PolicyName: 'DefaultVpcPolicy',
343 PolicyDocument: {
344 Statement: [
345 {
346 Effect: 'Allow',
347 Action: ['logs:*'],
348 Resource: cf.sub('arn:${AWS::Partition}:logs:*:*:*')
349 },
350 {
351 Effect: 'Allow',
352 Action: [
353 'ec2:describeVpcs',
354 'ec2:describeSubnets',
355 'ec2:describeRouteTables'
356 ],
357 Resource: '*'
358 }
359 ]
360 }
361 }
362 ]
363 }
364 };
365 case 'S3Inventory':
366 return {
367 Type: 'AWS::IAM::Role',
368 Properties: {
369 AssumeRolePolicyDocument: {
370 Statement: [
371 {
372 Effect: 'Allow',
373 Principal: { Service: 'lambda.amazonaws.com' },
374 Action: 'sts:AssumeRole'
375 }
376 ]
377 },
378 Policies: [
379 {
380 PolicyName: 'S3InventoryPolicy',
381 PolicyDocument: {
382 Statement: [
383 {
384 Effect: 'Allow',
385 Action: ['logs:*'],
386 Resource: cf.sub('arn:${AWS::Partition}:logs:*:*:*')
387 },
388 {
389 Effect: 'Allow',
390 Action: [
391 's3:PutInventoryConfiguration',
392 's3:DeleteInventoryConfiguration'
393 ],
394 Resource: '*'
395 }
396 ]
397 }
398 }
399 ]
400 }
401 }
402 default:
403 throw new Error('Unknown resource name');
404 }
405}
406
407function moreResources(params, customResource) {
408 switch (params.CustomResourceName) {
409 case 'S3NotificationTopicConfig':
410 customResource[`${params.LogicalName}SnsPolicy`] = {
411 Type: 'AWS::SNS::TopicPolicy',
412 Properties: {
413 PolicyDocument: {
414 Id: `${params.LogicalName}SnsPolicy`,
415 Version: '2012-10-17',
416 Statement: [
417 {
418 Sid: '',
419 Effect: 'Allow',
420 Principal: { Service: 's3.amazonaws.com'},
421 Action: 'SNS:Publish',
422 Resource: params.Properties.SnsTopicArn,
423 Condition: {
424 ArnLike: { 'aws:SourceArn': cf.sub('arn:${AWS::Partition}:s3:::${bucket}', { bucket: params.Properties.Bucket }) }
425 }
426 }
427 ]
428 },
429 Topics: [ params.Properties.SnsTopicArn ]
430 }
431 }
432 break;
433 default:
434 break;
435 }
436}