UNPKG

44.4 kBYAMLView Raw
1# Welcome to Serverless!
2#
3# This file is the main config file for your service.
4# It's very minimal at this point and uses default values.
5# You can always add more config options for more control.
6# We've included some commented out config examples here.
7# Just uncomment any of them to get that config option.
8#
9# For full config options, check the docs:
10# docs.serverless.com
11#
12# Happy Coding!
13
14service: sls-ci-${self:custom.serviceName}
15
16plugins:
17 - serverless-offline
18
19# You can pin your service to only deploy with a specific Serverless version
20# Check out our docs for more details
21# frameworkVersion: "=X.X.X"
22package:
23 exclude:
24 - tmp/**
25 - .git/**
26 - test/**
27 - services/**
28 include:
29 - "./services/${self:custom.serviceName}.yml"
30
31provider:
32 name: aws
33 runtime: nodejs12.x
34 stage: dev
35 region: ${self:custom.region}
36 profile: ${opt:aws-profile, self:custom.service.aws.profile}
37 # [HIGH] TODO: Clean this up, don't default to full access to Dynamo
38 iamRoleStatements:
39 - Effect: "Allow"
40 Action:
41 - "dynamodb:*"
42 Resource: "*"
43 environment:
44 SLS_BASE_URL: { "Fn::Join" : ["", [" https://", { "Ref" : "ApiGatewayRestApi" }, ".execute-api.${self:custom.region}.amazonaws.com/${self:provider.stage}" ] ] }
45 SLS_API_BASE: ${self:custom.apiBaseUrl}
46 SLS_STAGE: ${self:provider.stage}
47 CTX_SECURE: sec
48 ECR_REPO_NAME: ${self:custom.ecrRepoName}
49 SERVICE_NAME: ${self:custom.serviceName}
50 # TODO: Add github secret, token
51 BUILD_BUCKET_NAME: ${self:custom.buildBucketName}
52 SITE_BUCKET_NAME: ${self:custom.siteBucketName}
53 ASSET_BUCKET_NAME: ${self:custom.assetBucketName}
54 SECURE_BUCKET_NAME: ${self:custom.secureBucketName}
55 BUILDS_TABLE_NAME: ${self:custom.buildsTableName}
56 DEPLOYS_TABLE_NAME: ${self:custom.deploysTableName}
57 AUDITS_TABLE_NAME: ${self:custom.auditsTableName}
58 DOCUMENTS_TABLE_NAME: ${self:custom.documentsTableName}
59 SUBMISSIONS_TABLE_NAME: ${self:custom.submissionsTableName}
60 BUILD_PROJECT_NAME: ${self:custom.buildProjectName}
61 RECAPTCHA_SECRET_KEY: ${self:custom.recaptchaSecretKey}
62 # TODO: Add slack token for app/commands/etc
63 SLACK_WEBHOOK: ${self:custom.service.slack.webhook}
64 SLACK_CHANNEL: ${self:custom.service.slack.channel}
65 SLACK_USERNAME: ${self:custom.service.slack.username}
66 SLACK_ICON_EMOJI: ${self:custom.service.slack.icon_emoji}
67 SLACK_ICON_URL: ${self:custom.service.slack.icon_url}
68 SEND_EMAIL_FROM: ${self:custom.sendEmailFromAddress}
69 SEND_EMAIL_CONF: ${self:custom.sendEmailConfName}
70 PUBNUB_PUBLISH_KEY: ${self:custom.service.pubnub.publish_key}
71 PUBNUB_SUBSCRIBE_KEY: ${self:custom.service.pubnub.subscribe_key}
72 UPDATE_ROLE_ARN: { "Fn::Join" : ["", ["arn:aws:iam::", { "Ref" : "AWS::AccountId" }, ":role/${self:custom.serviceUpdateRoleName}" ] ] }
73 NOTIFY_SNS_ARN: { "Fn::Join" : ["", ["arn:aws:sns:${self:custom.region}:", { "Ref" : "AWS::AccountId" }, ":${self:custom.stackChangeTopic}" ] ] }
74
75custom:
76 serverless-offline:
77 port: 5000
78 prefix: dev
79 stage: dev
80 service: ${file(./services/${opt:service}.yml):${opt:service}}
81 apiVersion: v1
82 apiBaseUrl: "api/${self:custom.apiVersion}"
83 region: ${opt:region, self:custom.service.aws.region}
84 namespace: "${self:service}-${self:custom.region}"
85 serviceName: ${opt:service}
86 ecrRepoName: ${self:custom.namespace}-repo
87 buildBucketName: ${self:custom.namespace}-build-bucket
88 siteBucketName: ${self:custom.namespace}-site
89 assetBucketName: ${self:custom.namespace}-assets
90 secureBucketName: ${self:custom.namespace}-secure
91 buildsTableName: "${self:custom.namespace}-builds"
92 deploysTableName: "${self:custom.namespace}-deploys"
93 auditsTableName: "${self:custom.namespace}-audits"
94 documentsTableName: "${self:custom.namespace}-documents"
95 submissionsTableName: "${self:custom.namespace}-submissions"
96 buildProjectName: "${self:custom.namespace}-build-project"
97 # buildProjectSource: ${self:custom.service.source}
98 buildProjectSourceType: ${self:custom.service.source.type, 'GITHUB'}
99 buildProjectSourceBase: ${self:custom.service.source.base, 'https://github.com'}
100 sendEmailFromAddress: ${self:custom.service.email}
101 stackChangeTopic: "${self:custom.namespace}-stack-change"
102 buildChangeTopic: "${self:custom.namespace}-build-change"
103 buildProjectRoleName: "${self:custom.namespace}-build-project-role"
104 startBuildRoleName: "${self:custom.namespace}-start-build-role"
105 sendEmailRoleName: "${self:custom.namespace}-send-email-role"
106 sendEmailConfName: "${self:custom.namespace}-send-email-conf"
107 apiHandlerRoleName: "${self:custom.namespace}-api-handler-role"
108 notifySlackRoleName: "${self:custom.namespace}-notify-slack-role"
109 stackUpdateRoleName: "${self:custom.namespace}-update-stack-role"
110 serviceUpdateRoleName: "${self:custom.namespace}-update-service-role"
111 recaptchaSecretKey: "${self:custom.service.recaptcha_secret_key}"
112
113functions:
114 # email:
115 # handler: handler.sendEmail
116 # role: sendEmailRole
117 # events:
118 # - http:
119 # path: ${self:custom.apiBaseUrl}/email
120 # method: post
121 # cors: true
122
123 upload:
124 handler: handler.s3Upload
125 role: apiHandlerRole
126 events:
127 - http:
128 path: ${self:custom.apiBaseUrl}/upload
129 method: post
130 cors: true
131
132 # info:
133 # handler: handler.info
134 # role: apiHandlerRole
135 # events:
136 # - http:
137 # path: ${self:custom.apiBaseUrl}/info
138 # method: get
139 # cors: true
140
141 # builds-tags:
142 # handler: handler.builds_tags
143 # role: apiHandlerRole
144 # events:
145 # - http:
146 # path: ${self:custom.apiBaseUrl}/builds/tags
147 # method: get
148 # cors: true
149
150 # deploys-stacks:
151 # handler: handler.deploys_stacks
152 # role: apiHandlerRole
153 # events:
154 # - http:
155 # path: ${self:custom.apiBaseUrl}/deploys/stacks
156 # method: get
157 # cors: true
158
159 builds-deploy:
160 handler: handler.builds_deploy
161 role: apiHandlerRole
162 events:
163 - http:
164 path: ${self:custom.apiBaseUrl}/builds/{id}/deploy/{stack}
165 method: get
166 cors: true
167 request:
168 parameters:
169 paths:
170 id: true
171 stack: true
172
173 builds-index:
174 handler: handler.builds_index
175 role: apiHandlerRole
176 events:
177 - http:
178 path: ${self:custom.apiBaseUrl}/builds
179 method: get
180 cors: true
181
182 builds-show:
183 handler: handler.builds_show
184 role: apiHandlerRole
185 events:
186 - http:
187 path: ${self:custom.apiBaseUrl}/builds/{id}
188 method: get
189 cors: true
190 request:
191 parameters:
192 paths:
193 id: true
194
195 # builds-logs:
196 # handler: handler.builds_logs
197 # role: apiHandlerRole
198 # events:
199 # - http:
200 # path: ${self:custom.apiBaseUrl}/builds/{id}/logs
201 # method: get
202 # cors: true
203 # request:
204 # parameters:
205 # paths:
206 # id: true
207
208 # builds-create:
209 # handler: handler.builds_create
210 # role: apiHandlerRole
211 # events:
212 # - http:
213 # path: ${self:custom.apiBaseUrl}/builds
214 # method: post
215 # cors: true
216
217 builds-update:
218 handler: handler.builds_update
219 role: apiHandlerRole
220 events:
221 - http:
222 path: ${self:custom.apiBaseUrl}/builds/{id}
223 method: put
224 cors: true
225 request:
226 parameters:
227 paths:
228 id: true
229
230 # builds-destroy:
231 # handler: handler.builds_destroy
232 # role: apiHandlerRole
233 # events:
234 # - http:
235 # path: ${self:custom.apiBaseUrl}/builds/{id}
236 # method: delete
237 # cors: true
238 # request:
239 # parameters:
240 # paths:
241 # id: true
242
243 deploys-index:
244 handler: handler.deploys_index
245 role: apiHandlerRole
246 events:
247 - http:
248 path: ${self:custom.apiBaseUrl}/deploys
249 method: get
250 cors: true
251
252 deploys-show:
253 handler: handler.deploys_show
254 role: apiHandlerRole
255 events:
256 - http:
257 path: ${self:custom.apiBaseUrl}/deploys/{id}
258 method: get
259 cors: true
260 request:
261 parameters:
262 paths:
263 id: true
264
265 # deploys-create:
266 # handler: handler.deploys_create
267 # role: apiHandlerRole
268 # events:
269 # - http:
270 # path: ${self:custom.apiBaseUrl}/deploys
271 # method: post
272 # cors: true
273
274 deploys-update:
275 handler: handler.deploys_update
276 role: apiHandlerRole
277 events:
278 - http:
279 path: ${self:custom.apiBaseUrl}/deploys/{id}
280 method: put
281 cors: true
282 request:
283 parameters:
284 paths:
285 id: true
286
287 # deploys-destroy:
288 # handler: handler.deploys_destroy
289 # role: apiHandlerRole
290 # events:
291 # - http:
292 # path: ${self:custom.apiBaseUrl}/deploys/{id}
293 # method: delete
294 # cors: true
295 # request:
296 # parameters:
297 # paths:
298 # id: true
299
300 verify-recaptcha:
301 handler: handler.verifyRecaptcha
302 events:
303 - http:
304 path: ${self:custom.apiBaseUrl}/recaptcha
305 method: get
306 cors: true
307
308 submissions-index:
309 handler: handler.submissions_index
310 role: apiHandlerRole
311 events:
312 - http:
313 path: ${self:custom.apiBaseUrl}/submissions
314 method: get
315 cors: true
316
317 submissions-show:
318 handler: handler.submissions_show
319 role: apiHandlerRole
320 events:
321 - http:
322 path: ${self:custom.apiBaseUrl}/submissions/{id}
323 method: get
324 cors: true
325 request:
326 parameters:
327 paths:
328 id: true
329
330 submissions-create:
331 handler: handler.submissions_create
332 role: apiHandlerRole
333 events:
334 - http:
335 path: ${self:custom.apiBaseUrl}/submissions
336 method: post
337 cors: true
338
339 submissions-update:
340 handler: handler.submissions_update
341 role: apiHandlerRole
342 events:
343 - http:
344 path: ${self:custom.apiBaseUrl}/submissions/{id}
345 method: put
346 cors: true
347 request:
348 parameters:
349 paths:
350 id: true
351
352 submissions-download:
353 handler: handler.submissions_download
354 role: apiHandlerRole
355 events:
356 - http:
357 path: ${self:custom.apiBaseUrl}/submissions/{id}/download
358 method: get
359 cors: true
360 request:
361 parameters:
362 paths:
363 id: true
364
365 documents-index:
366 handler: handler.documents_index
367 role: apiHandlerRole
368 events:
369 - http:
370 path: ${self:custom.apiBaseUrl}/documents
371 method: get
372 cors: true
373
374 documents-audits:
375 handler: handler.documents_audits
376 role: apiHandlerRole
377 events:
378 - http:
379 path: ${self:custom.apiBaseUrl}/documents/{id}/audits
380 method: get
381 cors: true
382 request:
383 parameters:
384 paths:
385 id: true
386
387 documents-download:
388 handler: handler.documents_download
389 role: apiHandlerRole
390 events:
391 - http:
392 path: ${self:custom.apiBaseUrl}/documents/{id}/download
393 method: get
394 cors: true
395 request:
396 parameters:
397 paths:
398 id: true
399
400 documents-show:
401 handler: handler.documents_show
402 role: apiHandlerRole
403 events:
404 - http:
405 path: ${self:custom.apiBaseUrl}/documents/{id}
406 method: get
407 cors: true
408 request:
409 parameters:
410 paths:
411 id: true
412
413 documents-create:
414 handler: handler.documents_create
415 role: apiHandlerRole
416 events:
417 - http:
418 path: ${self:custom.apiBaseUrl}/documents
419 method: post
420 cors: true
421
422 documents-update:
423 handler: handler.documents_update
424 role: apiHandlerRole
425 events:
426 - http:
427 path: ${self:custom.apiBaseUrl}/documents/{id}
428 method: put
429 cors: true
430 request:
431 parameters:
432 paths:
433 id: true
434
435 documents-destroy:
436 handler: handler.documents_destroy
437 role: apiHandlerRole
438 events:
439 - http:
440 path: ${self:custom.apiBaseUrl}/documents/{id}
441 method: delete
442 cors: true
443 request:
444 parameters:
445 paths:
446 id: true
447
448 stack-change:
449 handler: handler.stackChange
450 role: notifySlackRole
451 events:
452 - sns: ${self:custom.stackChangeTopic}
453
454 build-change:
455 handler: handler.buildChange
456 role: updateStackRole
457 events:
458 - cloudwatchEvent:
459 description: 'CloudWatch Event triggered on a Code Build Project'
460 event:
461 source:
462 - "aws.codebuild"
463 detail-type:
464 - "CodeBuild Build State Change"
465 detail:
466 build-status:
467 - IN_PROGRESS
468 - SUCCEEDED
469 - FAILED
470 - STOPPED
471 project-name:
472 - ${self:custom.buildProjectName}
473
474 bitbucket-webhook:
475 handler: handler.bitbucketWebhook
476 role: startBuildRole
477 events:
478 - http:
479 path: bitbucket/webhook
480 method: post
481
482 github-webhook:
483 handler: handler.githubWebhook
484 role: startBuildRole
485 events:
486 - http:
487 path: github/webhook
488 method: post
489
490 # TODO: Add slack command & action function
491
492resources:
493 Conditions:
494 HasTargetEcs: { "Fn::Equals" : ["${self:custom.service.target}", "ecs"] }
495 HasTargetS3: { "Fn::Equals" : ["${self:custom.service.target}", "s3"] }
496 # HasEmailFrom: { "Fn::Not": [ "Fn::Equals": ["${self:custom.sendEmailFromAddress}", ""] ] }
497 Resources:
498 # TODO: Finish support for s3 static site? CloudFront? Or remove this?
499 siteBucket:
500 Type: 'AWS::S3::Bucket'
501 Properties:
502 BucketName: ${self:custom.siteBucketName}
503 AccessControl: PublicRead
504 WebsiteConfiguration:
505 IndexDocument: index.html
506 ErrorDocument: error.html
507 # DeletionPolicy: Retain
508
509 siteBucketPolicy:
510 Type: 'AWS::S3::BucketPolicy'
511 Properties:
512 Bucket: ${self:custom.siteBucketName}
513 PolicyDocument:
514 Id: PublicS3WebsitePolicy
515 Version: '2012-10-17'
516 Statement:
517 - Sid: PublicReadForGetBucketObjects
518 Effect: Allow
519 Principal: '*'
520 Action: 's3:GetObject'
521 Resource: 'arn:aws:s3:::${self:custom.siteBucketName}/*'
522
523 assetBucket:
524 Type: 'AWS::S3::Bucket'
525 Properties:
526 BucketName: ${self:custom.assetBucketName}
527 AccessControl: PublicRead
528 WebsiteConfiguration:
529 IndexDocument: index.html
530 ErrorDocument: error.html
531 # DeletionPolicy: Retain
532 CorsConfiguration:
533 CorsRules:
534 - MaxAge: 300
535 # ExposedHeaders: ['*']
536 AllowedHeaders: ['*']
537 AllowedOrigins: ['*']
538 AllowedMethods:
539 - HEAD
540 - GET
541 - PUT
542
543 assetBucketPolicy:
544 Type: 'AWS::S3::BucketPolicy'
545 Properties:
546 Bucket: ${self:custom.assetBucketName}
547 PolicyDocument:
548 Id: PublicS3WebsitePolicy
549 Version: '2012-10-17'
550 Statement:
551 - Sid: PublicReadForGetBucketObjects
552 Effect: Allow
553 Principal: '*'
554 Action: 's3:GetObject'
555 Resource: 'arn:aws:s3:::${self:custom.assetBucketName}/*'
556
557 buildBucket:
558 Type: AWS::S3::Bucket
559 Properties:
560 BucketName: ${self:custom.buildBucketName}
561
562 secureBucket:
563 Type: AWS::S3::Bucket
564 Properties:
565 BucketName: ${self:custom.secureBucketName}
566 AccessControl: Private
567 BucketEncryption:
568 ServerSideEncryptionConfiguration:
569 - ServerSideEncryptionByDefault:
570 SSEAlgorithm: AES256
571 CorsConfiguration:
572 CorsRules:
573 - MaxAge: 300
574 # ExposedHeaders: ['*']
575 AllowedHeaders: ['*']
576 AllowedOrigins: ['*']
577 AllowedMethods:
578 - HEAD
579 - GET
580 - PUT
581
582 ecrRepository:
583 Type: AWS::ECR::Repository
584 Properties:
585 RepositoryName: ${self:custom.ecrRepoName}
586
587 buildsTable:
588 Type: AWS::DynamoDB::Table
589 Properties:
590 TableName: ${self:custom.buildsTableName}
591 BillingMode: PAY_PER_REQUEST
592 AttributeDefinitions:
593 - AttributeName: id
594 AttributeType: 'S'
595 # - AttributeName: build_timestamp
596 # AttributeType: 'N'
597 # [HIGH] TODO: Use build_number? or commit SHA or another col for keys?
598 KeySchema:
599 - AttributeName: id
600 KeyType: HASH
601 # - AttributeName: build_timestamp
602 # KeyType: RANGE
603
604 deploysTable:
605 Type: AWS::DynamoDB::Table
606 Properties:
607 TableName: ${self:custom.deploysTableName}
608 BillingMode: PAY_PER_REQUEST
609 AttributeDefinitions:
610 - AttributeName: id
611 AttributeType: 'S'
612 # - AttributeName: deploy_timestamp
613 # AttributeType: 'N'
614 # [HIGH] TODO: Use build_number? or commit SHA or another col for keys?
615 KeySchema:
616 - AttributeName: id
617 KeyType: HASH
618 # - AttributeName: deploy_timestamp
619 # KeyType: RANGE
620
621 # documentsTable:
622 # Type: AWS::DynamoDB::Table
623 # Properties:
624 # TableName: ${self:custom.documentsTableName}
625 # BillingMode: PAY_PER_REQUEST
626 # AttributeDefinitions:
627 # - AttributeName: id
628 # AttributeType: 'S'
629 # - AttributeName: timestamp
630 # AttributeType: 'N'
631 # KeySchema:
632 # - AttributeName: id
633 # KeyType: HASH
634 # - AttributeName: timestamp
635 # KeyType: RANGE
636
637 # sendEmailConfigSet:
638 # Type: AWS::SES::ConfigurationSet
639 # Properties:
640 # Name: "${self:custom.sendEmailConfName}"
641
642 sendEmailRole:
643 Type: AWS::IAM::Role
644 Properties:
645 RoleName: ${self:custom.sendEmailRoleName}
646 AssumeRolePolicyDocument:
647 Version: '2012-10-17'
648 Statement:
649 - Effect: Allow
650 Principal:
651 Service:
652 - lambda.amazonaws.com
653 Action: sts:AssumeRole
654 Policies:
655 - PolicyName: sendEmail
656 PolicyDocument:
657 Version: '2012-10-17'
658 Statement:
659 - Effect: "Allow"
660 Action:
661 - "ses:SendEmail"
662 Resource:
663 - "*"
664 - Effect: "Allow"
665 Action:
666 - "logs:CreateLogStream"
667 Resource:
668 - { "Fn::Join" : ["", ["arn:aws:logs:${self:custom.region}:", { "Ref" : "AWS::AccountId" }, ":log-group:/aws/lambda/${self:service}-${self:provider.stage}-*:*" ] ] }
669 - Effect: "Allow"
670 Action:
671 - "logs:PutLogEvents"
672 Resource:
673 - { "Fn::Join" : ["", ["arn:aws:logs:${self:custom.region}:", { "Ref" : "AWS::AccountId" }, ":log-group:/aws/lambda/${self:service}-${self:provider.stage}-*:*:*" ] ] }
674
675 apiHandlerRole:
676 Type: AWS::IAM::Role
677 Properties:
678 RoleName: ${self:custom.apiHandlerRoleName}
679 AssumeRolePolicyDocument:
680 Version: '2012-10-17'
681 Statement:
682 - Effect: Allow
683 Principal:
684 Service:
685 - lambda.amazonaws.com
686 Action: sts:AssumeRole
687 Policies:
688 - PolicyName: notifySlack
689 PolicyDocument:
690 Version: '2012-10-17'
691 Statement:
692 # [HIGH] TODO: Refactor policies & access => min required perms
693 - Effect: "Allow"
694 Action:
695 - "s3:*"
696 Resource:
697 - 'arn:aws:s3:::${self:custom.secureBucketName}'
698 - 'arn:aws:s3:::${self:custom.secureBucketName}/*'
699 - 'arn:aws:s3:::${self:custom.buildBucketName}'
700 - 'arn:aws:s3:::${self:custom.buildBucketName}/*'
701 - 'arn:aws:s3:::${self:custom.siteBucketName}'
702 - 'arn:aws:s3:::${self:custom.siteBucketName}/*'
703 - 'arn:aws:s3:::${self:custom.assetBucketName}'
704 - 'arn:aws:s3:::${self:custom.assetBucketName}/*'
705 - Effect: "Allow"
706 Action:
707 - "logs:CreateLogStream"
708 Resource:
709 - { "Fn::Join" : ["", ["arn:aws:logs:${self:custom.region}:", { "Ref" : "AWS::AccountId" }, ":log-group:/aws/lambda/${self:service}-${self:provider.stage}-*:*" ] ] }
710 - Effect: "Allow"
711 Action:
712 - "logs:PutLogEvents"
713 Resource:
714 - { "Fn::Join" : ["", ["arn:aws:logs:${self:custom.region}:", { "Ref" : "AWS::AccountId" }, ":log-group:/aws/lambda/${self:service}-${self:provider.stage}-*:*:*" ] ] }
715 - Effect: Allow
716 Action:
717 - dynamodb:*
718 Resource:
719 - { "Fn::Join" : ["", ["arn:aws:dynamodb:${self:custom.region}:", { "Ref" : "AWS::AccountId" }, ":table/${self:custom.buildsTableName}"]]}
720 - { "Fn::Join" : ["", ["arn:aws:dynamodb:${self:custom.region}:", { "Ref" : "AWS::AccountId" }, ":table/${self:custom.deploysTableName}"]]}
721 - { "Fn::Join" : ["", ["arn:aws:dynamodb:${self:custom.region}:", { "Ref" : "AWS::AccountId" }, ":table/${self:custom.documentsTableName}"]]}
722 - { "Fn::Join" : ["", ["arn:aws:dynamodb:${self:custom.region}:", { "Ref" : "AWS::AccountId" }, ":table/${self:custom.submissionsTableName}"]]}
723 - { "Fn::Join" : ["", ["arn:aws:dynamodb:${self:custom.region}:", { "Ref" : "AWS::AccountId" }, ":table/${self:custom.auditsTableName}"]]}
724 - Effect: Allow
725 Action:
726 - codebuild:BatchGetBuilds
727 Resource:
728 - { "Fn::Join" : ["", ["arn:aws:codebuild:${self:custom.region}:", { "Ref" : "AWS::AccountId" }, ":project/${self:custom.buildProjectName}"]]}
729 - Effect: Allow
730 Action:
731 - "logs:GetLogEvents"
732 Resource:
733 - { "Fn::Join" : ["", ["arn:aws:logs:${self:custom.region}:", { "Ref" : "AWS::AccountId" }, ":log-group:/aws/codebuild/${self:custom.buildProjectName}:log-stream:*"]]}
734 - Effect: "Allow"
735 Action:
736 - "cloudformation:UpdateStack"
737 - "cloudformation:DescribeStacks"
738 Resource:
739 # - "*"
740 # [HIGH] TODO: Why was this failing perms for example-app?
741 - { "Fn::Join" : ["", ["arn:aws:cloudformation:${self:custom.region}:", { "Ref" : "AWS::AccountId" }, ":stack/${self:custom.serviceName}-*" ] ] }
742 - Effect: "Allow"
743 Action:
744 - "iam:PassRole"
745 Resource:
746 - { "Fn::Join" : ["", ["arn:aws:iam::", { "Ref" : "AWS::AccountId" }, ":role/${self:custom.serviceUpdateRoleName}" ] ] }
747
748 buildProject:
749 Type: AWS::CodeBuild::Project
750 DependsOn: buildProjectRole
751 Properties:
752 Name: ${self:custom.buildProjectName}
753 ServiceRole: ${self:custom.buildProjectRoleName}
754 Cache:
755 Type: S3
756 Location: ${self:custom.buildBucketName}
757 Artifacts:
758 Type: no_artifacts
759 Source:
760 # [HIGH] TODO: Dynamic source? GH vs BB
761 Location: ${self:custom.service.source.base}/${self:custom.service.source.repo}.git
762 Type: "${self:custom.service.source.type}"
763 Auth:
764 Type: OAUTH
765 Environment:
766 # [HIGH] TODO: Configure this
767 ComputeType: "BUILD_GENERAL1_SMALL"
768 PrivilegedMode: true
769 Image: "bowtie/docker-builder:v3"
770 Type: "LINUX_CONTAINER"
771 EnvironmentVariables:
772 - Name: CI
773 Value: 'true'
774 - Name: AWS_REGION
775 Value: ${self:custom.region}
776 - Name: AWS_ACCOUNT
777 Value: { "Ref" : "AWS::AccountId" }
778 - Name: REPO_SLUG
779 Value: ${self:custom.ecrRepoName}
780 - Name: AWS_BUCKET_NAME
781 Value: ${self:custom.buildBucketName}
782 - Name: AWS_SITE_BUCKET_NAME
783 Value: ${self:custom.siteBucketName}
784 - Name: IAM_ROLE_NAME
785 Value: ${self:custom.buildProjectRoleName}
786 - Name: GIT_REPO_NAME
787 Value: ${self:custom.service.source.repo, ""}
788 - Name: "${self:custom.service.source.type}_REPO"
789 Value: ${self:custom.service.source.repo, ""}
790 - Name: AIRBRAKE_PROJECT_ID
791 Value: ${self:custom.service.airbrake.id, ""}
792 - Name: AIRBRAKE_PROJECT_KEY
793 Value: ${self:custom.service.airbrake.key, ""}
794
795 buildProjectRole:
796 Type: AWS::IAM::Role
797 Properties:
798 RoleName: ${self:custom.buildProjectRoleName}
799 AssumeRolePolicyDocument:
800 Version: '2012-10-17'
801 Statement:
802 - Effect: "Allow"
803 Principal:
804 Service:
805 - codebuild.amazonaws.com
806 Action: sts:AssumeRole
807 Policies:
808 - PolicyName: buildProject
809 PolicyDocument:
810 Version: '2012-10-17'
811 Statement:
812 - Effect: Allow
813 Action:
814 - dynamodb:*
815 Resource:
816 - { "Fn::Join" : ["", ["arn:aws:dynamodb:${self:custom.region}:", { "Ref" : "AWS::AccountId" }, ":table/${self:custom.buildsTableName}"]]}
817 - { "Fn::Join" : ["", ["arn:aws:dynamodb:${self:custom.region}:", { "Ref" : "AWS::AccountId" }, ":table/${self:custom.deploysTableName}"]]}
818 - Effect: "Allow"
819 Action:
820 - "s3:*"
821 Resource:
822 - 'arn:aws:s3:::${self:custom.buildBucketName}'
823 - 'arn:aws:s3:::${self:custom.buildBucketName}/*'
824 - 'arn:aws:s3:::${self:custom.siteBucketName}'
825 - 'arn:aws:s3:::${self:custom.siteBucketName}/*'
826 - Effect: "Allow"
827 Action:
828 - "ecr:InitiateLayerUpload"
829 - "ecr:UploadLayerPart"
830 - "ecr:CompleteLayerUpload"
831 - "ecr:GetDownloadUrlForLayer"
832 - "ecr:BatchGetImage"
833 - "ecr:BatchCheckLayerAvailability"
834 - "ecr:PutImage"
835 - "ecr:GetAuthorizationToken"
836 - "logs:CreateLogGroup"
837 - "logs:CreateLogStream"
838 - "logs:PutLogEvents"
839 - "cloudfront:ListInvalidations"
840 - "cloudfront:GetInvalidation"
841 - "cloudfront:CreateInvalidation"
842 # [HIGH] TODO: Lock down this resource definition
843 Resource: "*"
844
845 startBuildRole:
846 Type: AWS::IAM::Role
847 Properties:
848 RoleName: ${self:custom.startBuildRoleName}
849 AssumeRolePolicyDocument:
850 Version: '2012-10-17'
851 Statement:
852 - Effect: Allow
853 Principal:
854 Service:
855 - lambda.amazonaws.com
856 Action: sts:AssumeRole
857 Policies:
858 - PolicyName: notifySlack
859 PolicyDocument:
860 Version: '2012-10-17'
861 Statement:
862 - Effect: "Allow"
863 Action:
864 - "ecr:BatchGetImage"
865 - "ecr:DescribeImages"
866 - "ecr:PutImage"
867 Resource: "*"
868 - Effect: "Allow"
869 Action:
870 - "logs:CreateLogStream"
871 Resource:
872 - { "Fn::Join" : ["", ["arn:aws:logs:${self:custom.region}:", { "Ref" : "AWS::AccountId" }, ":log-group:/aws/lambda/${self:service}-${self:provider.stage}-*:*" ] ] }
873 - Effect: "Allow"
874 Action:
875 - "logs:PutLogEvents"
876 Resource:
877 - { "Fn::Join" : ["", ["arn:aws:logs:${self:custom.region}:", { "Ref" : "AWS::AccountId" }, ":log-group:/aws/lambda/${self:service}-${self:provider.stage}-*:*:*" ] ] }
878 - Effect: "Allow"
879 Action:
880 - "codebuild:StartBuild"
881 - "codebuild:BatchGetBuilds"
882 Resource:
883 - { "Fn::Join" : ["", ["arn:aws:codebuild:${self:custom.region}:", { "Ref" : "AWS::AccountId" }, ":project/${self:custom.buildProjectName}"]]}
884 - Effect: Allow
885 Action:
886 - dynamodb:*
887 Resource:
888 - { "Fn::Join" : ["", ["arn:aws:dynamodb:${self:custom.region}:", { "Ref" : "AWS::AccountId" }, ":table/${self:custom.buildsTableName}"]]}
889 - { "Fn::Join" : ["", ["arn:aws:dynamodb:${self:custom.region}:", { "Ref" : "AWS::AccountId" }, ":table/${self:custom.deploysTableName}"]]}
890 - { "Fn::Join" : ["", ["arn:aws:dynamodb:${self:custom.region}:", { "Ref" : "AWS::AccountId" }, ":table/${self:custom.documentsTableName}"]]}
891 - { "Fn::Join" : ["", ["arn:aws:dynamodb:${self:custom.region}:", { "Ref" : "AWS::AccountId" }, ":table/${self:custom.submissionsTableName}"]]}
892 - { "Fn::Join" : ["", ["arn:aws:dynamodb:${self:custom.region}:", { "Ref" : "AWS::AccountId" }, ":table/${self:custom.auditsTableName}"]]}
893 - Effect: "Allow"
894 Action:
895 - "logs:GetLogEvents"
896 Resource:
897 - { "Fn::Join" : ["", ["arn:aws:logs:${self:custom.region}:", { "Ref" : "AWS::AccountId" }, ":log-group:/aws/codebuild/${self:custom.buildProjectName}:log-stream:*" ] ] }
898 - Effect: "Allow"
899 Action:
900 - "cloudformation:UpdateStack"
901 - "cloudformation:DescribeStacks"
902 Resource:
903 - { "Fn::Join" : ["", ["arn:aws:cloudformation:${self:custom.region}:", { "Ref" : "AWS::AccountId" }, ":stack/${self:custom.serviceName}-*" ] ] }
904 - Effect: "Allow"
905 Action:
906 - "iam:PassRole"
907 Resource:
908 - { "Fn::Join" : ["", ["arn:aws:iam::", { "Ref" : "AWS::AccountId" }, ":role/${self:custom.serviceUpdateRoleName}" ] ] }
909
910 notifySlackRole:
911 Type: AWS::IAM::Role
912 Properties:
913 RoleName: ${self:custom.notifySlackRoleName}
914 AssumeRolePolicyDocument:
915 Version: '2012-10-17'
916 Statement:
917 - Effect: Allow
918 Principal:
919 Service:
920 - lambda.amazonaws.com
921 Action: sts:AssumeRole
922 Policies:
923 - PolicyName: notifySlack
924 PolicyDocument:
925 Version: '2012-10-17'
926 Statement:
927 - Effect: Allow
928 Action:
929 - dynamodb:*
930 Resource:
931 - { "Fn::Join" : ["", ["arn:aws:dynamodb:${self:custom.region}:", { "Ref" : "AWS::AccountId" }, ":table/${self:custom.buildsTableName}"]]}
932 - { "Fn::Join" : ["", ["arn:aws:dynamodb:${self:custom.region}:", { "Ref" : "AWS::AccountId" }, ":table/${self:custom.deploysTableName}"]]}
933 - { "Fn::Join" : ["", ["arn:aws:dynamodb:${self:custom.region}:", { "Ref" : "AWS::AccountId" }, ":table/${self:custom.documentsTableName}"]]}
934 - { "Fn::Join" : ["", ["arn:aws:dynamodb:${self:custom.region}:", { "Ref" : "AWS::AccountId" }, ":table/${self:custom.submissionsTableName}"]]}
935 - { "Fn::Join" : ["", ["arn:aws:dynamodb:${self:custom.region}:", { "Ref" : "AWS::AccountId" }, ":table/${self:custom.auditsTableName}"]]}
936 - Effect: "Allow"
937 Action:
938 - "logs:CreateLogStream"
939 Resource:
940 - { "Fn::Join" : ["", ["arn:aws:logs:${self:custom.region}:", { "Ref" : "AWS::AccountId" }, ":log-group:/aws/lambda/${self:service}-${self:provider.stage}-*:*" ] ] }
941 - Effect: "Allow"
942 Action:
943 - "logs:PutLogEvents"
944 Resource:
945 - { "Fn::Join" : ["", ["arn:aws:logs:${self:custom.region}:", { "Ref" : "AWS::AccountId" }, ":log-group:/aws/lambda/${self:service}-${self:provider.stage}-*:*:*" ] ] }
946 - Effect: "Allow"
947 Action:
948 - "ecs:ListTaskDefinitions"
949 - "cloudformation:DescribeStacks"
950 Resource: "*"
951 - Effect: "Allow"
952 Action:
953 - "SNS:Publish"
954 Resource:
955 - { "Fn::Join" : ["", ["arn:aws:sns:${self:custom.region}:", { "Ref" : "AWS::AccountId" }, ":${self:custom.stackChangeTopic}" ] ] }
956 - Effect: "Allow"
957 Action:
958 - "cloudwatch:DescribeAlarms"
959 - "cloudwatch:PutMetricAlarm"
960 Resource:
961 - { "Fn::Join" : ["", ["arn:aws:cloudwatch:${self:custom.region}:", { "Ref" : "AWS::AccountId" }, ":*/${self:custom.serviceName}*" ] ] }
962 - Effect: "Allow"
963 Action:
964 - "ecs:List*"
965 - "ecs:RunTask"
966 - "ecs:Describe*"
967 - "ecs:RegisterTaskDefinition"
968 - "ecs:DeregisterTaskDefinition"
969 - "ecs:UpdateService"
970 Resource:
971 - { "Fn::Join" : ["", ["arn:aws:ecs:${self:custom.region}:", { "Ref" : "AWS::AccountId" }, ":*/${self:custom.serviceName}*" ] ] }
972 - Effect: "Allow"
973 Action:
974 - "iam:AttachRolePolicy"
975 - "iam:CreateRole"
976 - "iam:GetPolicy"
977 - "iam:GetPolicyVersion"
978 - "iam:GetRole"
979 - "iam:PassRole"
980 - "iam:ListAttachedRolePolicies"
981 - "iam:ListRoles"
982 - "iam:ListGroups"
983 - "iam:ListUsers"
984 Resource:
985 - { "Fn::Join" : ["", ["arn:aws:iam::", { "Ref" : "AWS::AccountId" }, ":user/${self:custom.serviceName}*" ] ] }
986 - { "Fn::Join" : ["", ["arn:aws:iam::", { "Ref" : "AWS::AccountId" }, ":role/${self:custom.serviceName}*" ] ] }
987 - { "Fn::Join" : ["", ["arn:aws:iam::", { "Ref" : "AWS::AccountId" }, ":group/${self:custom.serviceName}*" ] ] }
988 - { "Fn::Join" : ["", ["arn:aws:iam::", { "Ref" : "AWS::AccountId" }, ":policy/${self:custom.serviceName}*" ] ] }
989 - { "Fn::Join" : ["", ["arn:aws:iam::", { "Ref" : "AWS::AccountId" }, ":instance-profile/${self:custom.serviceName}*" ] ] }
990
991 updateServiceRole:
992 Type: AWS::IAM::Role
993 Properties:
994 RoleName: ${self:custom.serviceUpdateRoleName}
995 AssumeRolePolicyDocument:
996 Version: '2012-10-17'
997 Statement:
998 - Effect: Allow
999 Principal:
1000 Service:
1001 - cloudformation.amazonaws.com
1002 Action: sts:AssumeRole
1003 Policies:
1004 - PolicyName: updateService
1005 PolicyDocument:
1006 Version: '2012-10-17'
1007 Statement:
1008 - Effect: Allow
1009 Action:
1010 - dynamodb:*
1011 Resource:
1012 - { "Fn::Join" : ["", ["arn:aws:dynamodb:${self:custom.region}:", { "Ref" : "AWS::AccountId" }, ":table/${self:custom.buildsTableName}"]]}
1013 - { "Fn::Join" : ["", ["arn:aws:dynamodb:${self:custom.region}:", { "Ref" : "AWS::AccountId" }, ":table/${self:custom.deploysTableName}"]]}
1014 - { "Fn::Join" : ["", ["arn:aws:dynamodb:${self:custom.region}:", { "Ref" : "AWS::AccountId" }, ":table/${self:custom.documentsTableName}"]]}
1015 - { "Fn::Join" : ["", ["arn:aws:dynamodb:${self:custom.region}:", { "Ref" : "AWS::AccountId" }, ":table/${self:custom.submissionsTableName}"]]}
1016 - { "Fn::Join" : ["", ["arn:aws:dynamodb:${self:custom.region}:", { "Ref" : "AWS::AccountId" }, ":table/${self:custom.auditsTableName}"]]}
1017 - Effect: "Allow"
1018 Action:
1019 - "SNS:Publish"
1020 Resource:
1021 - { "Fn::Join" : ["", ["arn:aws:sns:${self:custom.region}:", { "Ref" : "AWS::AccountId" }, ":${self:custom.stackChangeTopic}" ] ] }
1022 - Effect: "Allow"
1023 Action:
1024 - "application-autoscaling:Describe*"
1025 - "application-autoscaling:PutScalingPolicy"
1026 - "application-autoscaling:DeleteScalingPolicy"
1027 - "application-autoscaling:RegisterScalableTarget"
1028 - "cloudwatch:DescribeAlarms"
1029 - "cloudwatch:PutMetricAlarm"
1030 - "ecs:List*"
1031 - "ecs:Describe*"
1032 - "ecs:RegisterTaskDefinition"
1033 - "ecs:DeregisterTaskDefinition"
1034 - "ecs:UpdateService"
1035 - "iam:AttachRolePolicy"
1036 - "iam:CreateRole"
1037 - "iam:GetPolicy"
1038 - "iam:GetPolicyVersion"
1039 - "iam:GetRole"
1040 - "iam:PassRole"
1041 - "iam:ListAttachedRolePolicies"
1042 - "iam:ListRoles"
1043 - "iam:ListGroups"
1044 - "iam:ListUsers"
1045 Resource: "*"
1046
1047 updateStackRole:
1048 Type: AWS::IAM::Role
1049 Properties:
1050 RoleName: ${self:custom.stackUpdateRoleName}
1051 AssumeRolePolicyDocument:
1052 Version: '2012-10-17'
1053 Statement:
1054 - Effect: Allow
1055 Principal:
1056 Service:
1057 - lambda.amazonaws.com
1058 Action: sts:AssumeRole
1059 Policies:
1060 - PolicyName: updateStack
1061 PolicyDocument:
1062 Version: '2012-10-17'
1063 Statement:
1064 - Effect: Allow
1065 Action:
1066 - dynamodb:*
1067 Resource:
1068 - { "Fn::Join" : ["", ["arn:aws:dynamodb:${self:custom.region}:", { "Ref" : "AWS::AccountId" }, ":table/${self:custom.buildsTableName}"]]}
1069 - { "Fn::Join" : ["", ["arn:aws:dynamodb:${self:custom.region}:", { "Ref" : "AWS::AccountId" }, ":table/${self:custom.deploysTableName}"]]}
1070 - { "Fn::Join" : ["", ["arn:aws:dynamodb:${self:custom.region}:", { "Ref" : "AWS::AccountId" }, ":table/${self:custom.documentsTableName}"]]}
1071 - { "Fn::Join" : ["", ["arn:aws:dynamodb:${self:custom.region}:", { "Ref" : "AWS::AccountId" }, ":table/${self:custom.submissionsTableName}"]]}
1072 - { "Fn::Join" : ["", ["arn:aws:dynamodb:${self:custom.region}:", { "Ref" : "AWS::AccountId" }, ":table/${self:custom.auditsTableName}"]]}
1073 - Effect: "Allow"
1074 Action:
1075 - "ecr:BatchGetImage"
1076 Resource: "*"
1077 - Effect: "Allow"
1078 Action:
1079 - "logs:CreateLogStream"
1080 Resource:
1081 - { "Fn::Join" : ["", ["arn:aws:logs:${self:custom.region}:", { "Ref" : "AWS::AccountId" }, ":log-group:/aws/lambda/${self:service}-${self:provider.stage}-*:*" ] ] }
1082 - Effect: "Allow"
1083 Action:
1084 - "logs:PutLogEvents"
1085 Resource:
1086 - { "Fn::Join" : ["", ["arn:aws:logs:${self:custom.region}:", { "Ref" : "AWS::AccountId" }, ":log-group:/aws/lambda/${self:service}-${self:provider.stage}-*:*:*" ] ] }
1087 - Effect: "Allow"
1088 Action:
1089 - "logs:GetLogEvents"
1090 Resource:
1091 - { "Fn::Join" : ["", ["arn:aws:logs:${self:custom.region}:", { "Ref" : "AWS::AccountId" }, ":log-group:/aws/codebuild/${self:custom.buildProjectName}:log-stream:*" ] ] }
1092 - Effect: "Allow"
1093 Action:
1094 - "cloudformation:UpdateStack"
1095 - "cloudformation:DescribeStacks"
1096 Resource:
1097 # - "*"
1098 # [HIGH] TODO: Why was this failing perms for example-app?
1099 - { "Fn::Join" : ["", ["arn:aws:cloudformation:${self:custom.region}:", { "Ref" : "AWS::AccountId" }, ":stack/${self:custom.serviceName}-*" ] ] }
1100 - Effect: "Allow"
1101 Action:
1102 - "iam:PassRole"
1103 Resource:
1104 - { "Fn::Join" : ["", ["arn:aws:iam::", { "Ref" : "AWS::AccountId" }, ":role/${self:custom.serviceUpdateRoleName}" ] ] }
1105 - Effect: "Allow"
1106 Action:
1107 - "ecr:BatchGetImage"
1108 - "ecr:DescribeImages"
1109 Resource: "*"
1110 Outputs:
1111 buildBucket:
1112 Description: 'buildBucket value'
1113 Value: { "Ref": "buildBucket" }
1114 Export:
1115 Name: "${self:custom.serviceName}-buildBucket"
1116 siteBucket:
1117 Description: 'siteBucket value'
1118 Value: { "Ref": "siteBucket" }
1119 Export:
1120 Name: "${self:custom.serviceName}-siteBucket"
1121 buildProject:
1122 Description: 'buildProject value'
1123 Value: { "Ref": "buildProject" }
1124 Export:
1125 Name: "${self:custom.serviceName}-buildProject"
1126 buildProjectRole:
1127 Description: 'buildProjectRole value'
1128 Value: { "Ref": "buildProjectRole" }
1129 Export:
1130 Name: "${self:custom.serviceName}-buildProjectRole"
1131 buildsTable:
1132 Description: 'buildsTable value'
1133 Value: { "Ref": "buildsTable" }
1134 Export:
1135 Name: "${self:custom.serviceName}-buildsTable"
1136 deploysTable:
1137 Description: 'deploysTable value'
1138 Value: { "Ref": "deploysTable" }
1139 Export:
1140 Name: "${self:custom.serviceName}-deploysTable"
1141 ecrRepository:
1142 Description: 'ecrRepository value'
1143 Value: { "Ref": "ecrRepository" }
1144 Export:
1145 Name: "${self:custom.serviceName}-ecrRepository"
1146 notifySlackRole:
1147 Description: 'notifySlackRole value'
1148 Value: { "Ref": "notifySlackRole" }
1149 Export:
1150 Name: "${self:custom.serviceName}-notifySlackRole"
1151 startBuildRole:
1152 Description: 'startBuildRole value'
1153 Value: { "Ref": "startBuildRole" }
1154 Export:
1155 Name: "${self:custom.serviceName}-startBuildRole"
1156 updateServiceRole:
1157 Description: 'updateServiceRole value'
1158 Value: { "Ref": "updateServiceRole" }
1159 Export:
1160 Name: "${self:custom.serviceName}-updateServiceRole"
1161 updateStackRole:
1162 Description: 'updateStackRole value'
1163 Value: { "Ref": "updateStackRole" }
1164 Export:
1165 Name: "${self:custom.serviceName}-updateStackRole"
1166 notifySnsTopic:
1167 Description: 'notifySnsTopic value'
1168 Value: { "Fn::Join" : ["", ["arn:aws:sns:${self:custom.region}:", { "Ref" : "AWS::AccountId" }, ":${self:custom.stackChangeTopic}" ] ] }
1169 Export:
1170 Name: "${self:custom.serviceName}-notifySnsTopic"