@bowtie/sls
Version:
Serverless helpers & utilities
527 lines (498 loc) • 20.1 kB
YAML
# Welcome to Serverless!
#
# This file is the main config file for your service.
# It's very minimal at this point and uses default values.
# You can always add more config options for more control.
# We've included some commented out config examples here.
# Just uncomment any of them to get that config option.
#
# For full config options, check the docs:
# docs.serverless.com
#
# Happy Coding!
service: sls-ci-${self:custom.serviceName}-documents
plugins:
- serverless-offline
- serverless-webpack
# You can pin your service to only deploy with a specific Serverless version
# Check out our docs for more details
# frameworkVersion: "=X.X.X"
package:
exclude:
- tmp/**
- .git/**
- test/**
- services/**
include:
- "./services/${self:custom.serviceName}.yml"
provider:
name: aws
runtime: nodejs12.x
stage: dev
region: ${self:custom.region}
profile: ${opt:aws-profile, self:custom.service.aws.profile}
apiGateway:
restApiId:
Fn::ImportValue: '${self:custom.serviceName}-restApiId'
restApiRootResourceId:
Fn::ImportValue: '${self:custom.serviceName}-restApiRootResourceId'
# restApiResources:
# users: { 'Fn::ImportValue': '${self:custom.serviceName}-ApiGatewayResourceUsers' }
# users/me:
# Fn::ImportValue: '${self:custom.serviceName}-ApiGatewayResourceUsersMe'
# [HIGH] TODO: Clean this up, don't default to full access to Dynamo
iamRoleStatements:
- Effect: "Allow"
Action:
- "dynamodb:*"
Resource: "*"
environment:
SLS_BASE_URL: { "Fn::Join" : ["", [" https://", { "Fn::ImportValue" : "${self:custom.serviceName}-restApiId" }, ".execute-api.${self:custom.region}.amazonaws.com/${self:provider.stage}" ] ] }
SLS_API_BASE: ${self:custom.apiBaseUrl}
SLS_STAGE: ${self:provider.stage}
CTX_SECURE: sec
ECR_REPO_NAME: ${self:custom.ecrRepoName}
SERVICE_NAME: ${self:custom.serviceName}
# TODO: Add github secret, token
BUILD_BUCKET_NAME: ${self:custom.buildBucketName}
SITE_BUCKET_NAME: ${self:custom.siteBucketName}
ASSET_BUCKET_NAME: ${self:custom.assetBucketName}
SECURE_BUCKET_NAME: ${self:custom.secureBucketName}
BUILDS_TABLE_NAME: ${self:custom.buildsTableName}
DEPLOYS_TABLE_NAME: ${self:custom.deploysTableName}
AUDITS_TABLE_NAME: ${self:custom.auditsTableName}
DOCUMENTS_TABLE_NAME: ${self:custom.documentsTableName}
SUBMISSIONS_TABLE_NAME: ${self:custom.submissionsTableName}
BUILD_PROJECT_NAME: ${self:custom.buildProjectName}
RECAPTCHA_SECRET_KEY: ${self:custom.recaptchaSecretKey}
# TODO: Add slack token for app/commands/etc
SLACK_WEBHOOK: ${self:custom.service.slack.webhook}
SLACK_CHANNEL: ${self:custom.service.slack.channel}
SLACK_USERNAME: ${self:custom.service.slack.username}
SLACK_ICON_EMOJI: ${self:custom.service.slack.icon_emoji}
SLACK_ICON_URL: ${self:custom.service.slack.icon_url}
SEND_EMAIL_FROM: ${self:custom.sendEmailFromAddress}
SEND_EMAIL_CONF: ${self:custom.sendEmailConfName}
PUBNUB_PUBLISH_KEY: ${self:custom.service.pubnub.publish_key}
PUBNUB_SUBSCRIBE_KEY: ${self:custom.service.pubnub.subscribe_key}
UPDATE_ROLE_ARN: { "Fn::Join" : ["", ["arn:aws:iam::", { "Ref" : "AWS::AccountId" }, ":role/${self:custom.serviceUpdateRoleName}" ] ] }
NOTIFY_SNS_ARN: { "Fn::Join" : ["", ["arn:aws:sns:${self:custom.region}:", { "Ref" : "AWS::AccountId" }, ":${self:custom.stackChangeTopic}" ] ] }
custom:
serverless-offline:
port: 5000
prefix: dev
stage: dev
service: ${file(./services/${opt:service}.yml):${opt:service}}
apiVersion: v1
apiBaseUrl: "api/${self:custom.apiVersion}"
region: ${opt:region, self:custom.service.aws.region}
namespace: "${self:service}-${self:custom.region}"
serviceName: ${opt:service}
ecrRepoName: ${self:custom.namespace}-repo
buildBucketName: ${self:custom.namespace}-build-bucket
siteBucketName: ${self:custom.namespace}-site
assetBucketName: ${self:custom.namespace}-assets
secureBucketName: ${self:custom.namespace}-secure
buildsTableName: "${self:custom.namespace}-builds"
deploysTableName: "${self:custom.namespace}-deploys"
auditsTableName: "${self:custom.namespace}-audits"
documentsTableName: "${self:custom.namespace}-documents"
submissionsTableName: "${self:custom.namespace}-submissions"
buildProjectName: "${self:custom.namespace}-build-project"
# buildProjectSource: ${self:custom.service.source}
buildProjectSourceType: ${self:custom.service.source.type, 'GITHUB'}
buildProjectSourceBase: ${self:custom.service.source.base, 'https://github.com'}
sendEmailFromAddress: ${self:custom.service.email}
stackChangeTopic: "${self:custom.namespace}-stack-change"
buildChangeTopic: "${self:custom.namespace}-build-change"
buildProjectRoleName: "${self:custom.namespace}-build-project-role"
startBuildRoleName: "${self:custom.namespace}-start-build-role"
sendEmailRoleName: "${self:custom.namespace}-send-email-role"
sendEmailConfName: "${self:custom.namespace}-send-email-conf"
apiDocsRoleName: "${self:custom.namespace}-api-docs-role"
notifySlackRoleName: "${self:custom.namespace}-notify-slack-role"
stackUpdateRoleName: "${self:custom.namespace}-update-stack-role"
serviceUpdateRoleName: "${self:custom.namespace}-update-service-role"
recaptchaSecretKey: "${self:custom.service.recaptcha_secret_key}"
functions:
documents-index:
handler: handler.documents_index
role: apiDocsRole
events:
- http:
path: ${self:custom.apiBaseUrl}/documents
method: get
cors: true
documents-audits:
handler: handler.documents_audits
role: apiDocsRole
events:
- http:
path: ${self:custom.apiBaseUrl}/documents/{id}/audits
method: get
cors: true
request:
parameters:
paths:
id: true
documents-download:
handler: handler.documents_download
role: apiDocsRole
events:
- http:
path: ${self:custom.apiBaseUrl}/documents/{id}/download
method: get
cors: true
request:
parameters:
paths:
id: true
documents-show:
handler: handler.documents_show
role: apiDocsRole
events:
- http:
path: ${self:custom.apiBaseUrl}/documents/{id}
method: get
cors: true
request:
parameters:
paths:
id: true
documents-create:
handler: handler.documents_create
role: apiDocsRole
events:
- http:
path: ${self:custom.apiBaseUrl}/documents
method: post
cors: true
documents-update:
handler: handler.documents_update
role: apiDocsRole
events:
- http:
path: ${self:custom.apiBaseUrl}/documents/{id}
method: put
cors: true
request:
parameters:
paths:
id: true
documents-destroy:
handler: handler.documents_destroy
role: apiDocsRole
events:
- http:
path: ${self:custom.apiBaseUrl}/documents/{id}
method: delete
cors: true
request:
parameters:
paths:
id: true
# TODO: Add slack command & action function
resources:
Conditions:
HasTargetEcs: { "Fn::Equals" : ["${self:custom.service.target}", "ecs"] }
HasTargetS3: { "Fn::Equals" : ["${self:custom.service.target}", "s3"] }
# HasEmailFrom: { "Fn::Not": [ "Fn::Equals": ["${self:custom.sendEmailFromAddress}", ""] ] }
Resources:
# TODO: Finish support for s3 static site? CloudFront? Or remove this?
# siteBucket:
# Type: 'AWS::S3::Bucket'
# Properties:
# BucketName: ${self:custom.siteBucketName}
# AccessControl: PublicRead
# WebsiteConfiguration:
# IndexDocument: index.html
# ErrorDocument: error.html
# # DeletionPolicy: Retain
# siteBucketPolicy:
# Type: 'AWS::S3::BucketPolicy'
# Properties:
# Bucket: ${self:custom.siteBucketName}
# PolicyDocument:
# Id: PublicS3WebsitePolicy
# Version: '2012-10-17'
# Statement:
# - Sid: PublicReadForGetBucketObjects
# Effect: Allow
# Principal: '*'
# Action: 's3:GetObject'
# Resource: 'arn:aws:s3:::${self:custom.siteBucketName}/*'
# assetBucket:
# Type: 'AWS::S3::Bucket'
# Properties:
# BucketName: ${self:custom.assetBucketName}
# AccessControl: PublicRead
# WebsiteConfiguration:
# IndexDocument: index.html
# ErrorDocument: error.html
# # DeletionPolicy: Retain
# CorsConfiguration:
# CorsRules:
# - MaxAge: 300
# # ExposedHeaders: ['*']
# AllowedHeaders: ['*']
# AllowedOrigins: ['*']
# AllowedMethods:
# - HEAD
# - GET
# - PUT
# assetBucketPolicy:
# Type: 'AWS::S3::BucketPolicy'
# Properties:
# Bucket: ${self:custom.assetBucketName}
# PolicyDocument:
# Id: PublicS3WebsitePolicy
# Version: '2012-10-17'
# Statement:
# - Sid: PublicReadForGetBucketObjects
# Effect: Allow
# Principal: '*'
# Action: 's3:GetObject'
# Resource: 'arn:aws:s3:::${self:custom.assetBucketName}/*'
# buildBucket:
# Type: AWS::S3::Bucket
# Properties:
# BucketName: ${self:custom.buildBucketName}
# secureBucket:
# Type: AWS::S3::Bucket
# Properties:
# BucketName: ${self:custom.secureBucketName}
# AccessControl: Private
# BucketEncryption:
# ServerSideEncryptionConfiguration:
# - ServerSideEncryptionByDefault:
# SSEAlgorithm: AES256
# CorsConfiguration:
# CorsRules:
# - MaxAge: 300
# # ExposedHeaders: ['*']
# AllowedHeaders: ['*']
# AllowedOrigins: ['*']
# AllowedMethods:
# - HEAD
# - GET
# - PUT
# ecrRepository:
# Type: AWS::ECR::Repository
# Properties:
# RepositoryName: ${self:custom.ecrRepoName}
# buildsTable:
# Type: AWS::DynamoDB::Table
# Properties:
# TableName: ${self:custom.buildsTableName}
# BillingMode: PAY_PER_REQUEST
# AttributeDefinitions:
# - AttributeName: id
# AttributeType: 'S'
# # - AttributeName: build_timestamp
# # AttributeType: 'N'
# # [HIGH] TODO: Use build_number? or commit SHA or another col for keys?
# KeySchema:
# - AttributeName: id
# KeyType: HASH
# # - AttributeName: build_timestamp
# # KeyType: RANGE
# deploysTable:
# Type: AWS::DynamoDB::Table
# Properties:
# TableName: ${self:custom.deploysTableName}
# BillingMode: PAY_PER_REQUEST
# AttributeDefinitions:
# - AttributeName: id
# AttributeType: 'S'
# # - AttributeName: deploy_timestamp
# # AttributeType: 'N'
# # [HIGH] TODO: Use build_number? or commit SHA or another col for keys?
# KeySchema:
# - AttributeName: id
# KeyType: HASH
# # - AttributeName: deploy_timestamp
# # KeyType: RANGE
# documentsTable:
# Type: AWS::DynamoDB::Table
# Properties:
# TableName: ${self:custom.documentsTableName}
# BillingMode: PAY_PER_REQUEST
# AttributeDefinitions:
# - AttributeName: id
# AttributeType: 'S'
# - AttributeName: timestamp
# AttributeType: 'N'
# KeySchema:
# - AttributeName: id
# KeyType: HASH
# - AttributeName: timestamp
# KeyType: RANGE
# sendEmailConfigSet:
# Type: AWS::SES::ConfigurationSet
# Properties:
# Name: "${self:custom.sendEmailConfName}"
# sendEmailRole:
# Type: AWS::IAM::Role
# Properties:
# RoleName: ${self:custom.sendEmailRoleName}
# AssumeRolePolicyDocument:
# Version: '2012-10-17'
# Statement:
# - Effect: Allow
# Principal:
# Service:
# - lambda.amazonaws.com
# Action: sts:AssumeRole
# Policies:
# - PolicyName: sendEmail
# PolicyDocument:
# Version: '2012-10-17'
# Statement:
# - Effect: "Allow"
# Action:
# - "ses:SendEmail"
# Resource:
# - "*"
# - Effect: "Allow"
# Action:
# - "logs:CreateLogStream"
# Resource:
# - { "Fn::Join" : ["", ["arn:aws:logs:${self:custom.region}:", { "Ref" : "AWS::AccountId" }, ":log-group:/aws/lambda/${self:service}-${self:provider.stage}-*:*" ] ] }
# - Effect: "Allow"
# Action:
# - "logs:PutLogEvents"
# Resource:
# - { "Fn::Join" : ["", ["arn:aws:logs:${self:custom.region}:", { "Ref" : "AWS::AccountId" }, ":log-group:/aws/lambda/${self:service}-${self:provider.stage}-*:*:*" ] ] }
apiDocsRole:
Type: AWS::IAM::Role
Properties:
RoleName: ${self:custom.apiDocsRoleName}
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Principal:
Service:
- lambda.amazonaws.com
Action: sts:AssumeRole
Policies:
- PolicyName: notifySlack
PolicyDocument:
Version: '2012-10-17'
Statement:
# [HIGH] TODO: Refactor policies & access => min required perms
- Effect: "Allow"
Action:
- "s3:*"
Resource:
- 'arn:aws:s3:::${self:custom.secureBucketName}'
- 'arn:aws:s3:::${self:custom.secureBucketName}/*'
# - 'arn:aws:s3:::${self:custom.buildBucketName}'
# - 'arn:aws:s3:::${self:custom.buildBucketName}/*'
# - 'arn:aws:s3:::${self:custom.siteBucketName}'
# - 'arn:aws:s3:::${self:custom.siteBucketName}/*'
# - 'arn:aws:s3:::${self:custom.assetBucketName}'
# - 'arn:aws:s3:::${self:custom.assetBucketName}/*'
- Effect: "Allow"
Action:
- "logs:CreateLogStream"
Resource:
- { "Fn::Join" : ["", ["arn:aws:logs:${self:custom.region}:", { "Ref" : "AWS::AccountId" }, ":log-group:/aws/lambda/${self:service}-${self:provider.stage}-*:*" ] ] }
- Effect: "Allow"
Action:
- "logs:PutLogEvents"
Resource:
- { "Fn::Join" : ["", ["arn:aws:logs:${self:custom.region}:", { "Ref" : "AWS::AccountId" }, ":log-group:/aws/lambda/${self:service}-${self:provider.stage}-*:*:*" ] ] }
- Effect: Allow
Action:
- dynamodb:*
Resource:
# - { "Fn::Join" : ["", ["arn:aws:dynamodb:${self:custom.region}:", { "Ref" : "AWS::AccountId" }, ":table/${self:custom.buildsTableName}"]]}
# - { "Fn::Join" : ["", ["arn:aws:dynamodb:${self:custom.region}:", { "Ref" : "AWS::AccountId" }, ":table/${self:custom.deploysTableName}"]]}
- { "Fn::Join" : ["", ["arn:aws:dynamodb:${self:custom.region}:", { "Ref" : "AWS::AccountId" }, ":table/${self:custom.documentsTableName}"]]}
# - { "Fn::Join" : ["", ["arn:aws:dynamodb:${self:custom.region}:", { "Ref" : "AWS::AccountId" }, ":table/${self:custom.submissionsTableName}"]]}
- { "Fn::Join" : ["", ["arn:aws:dynamodb:${self:custom.region}:", { "Ref" : "AWS::AccountId" }, ":table/${self:custom.auditsTableName}"]]}
# - Effect: Allow
# Action:
# - codebuild:BatchGetBuilds
# Resource:
# - { "Fn::Join" : ["", ["arn:aws:codebuild:${self:custom.region}:", { "Ref" : "AWS::AccountId" }, ":project/${self:custom.buildProjectName}"]]}
- Effect: Allow
Action:
- "logs:GetLogEvents"
Resource:
- { "Fn::Join" : ["", ["arn:aws:logs:${self:custom.region}:", { "Ref" : "AWS::AccountId" }, ":log-group:/aws/codebuild/${self:custom.buildProjectName}:log-stream:*"]]}
# - Effect: "Allow"
# Action:
# - "cloudformation:UpdateStack"
# - "cloudformation:DescribeStacks"
# Resource:
# # - "*"
# # [HIGH] TODO: Why was this failing perms for example-app?
# - { "Fn::Join" : ["", ["arn:aws:cloudformation:${self:custom.region}:", { "Ref" : "AWS::AccountId" }, ":stack/${self:custom.serviceName}-*" ] ] }
# - Effect: "Allow"
# Action:
# - "iam:PassRole"
# Resource:
# - { "Fn::Join" : ["", ["arn:aws:iam::", { "Ref" : "AWS::AccountId" }, ":role/${self:custom.serviceUpdateRoleName}" ] ] }
# Outputs:
# buildBucket:
# Description: 'buildBucket value'
# Value: { "Ref": "buildBucket" }
# Export:
# Name: "${self:custom.serviceName}-buildBucket"
# siteBucket:
# Description: 'siteBucket value'
# Value: { "Ref": "siteBucket" }
# Export:
# Name: "${self:custom.serviceName}-siteBucket"
# buildProject:
# Description: 'buildProject value'
# Value: { "Ref": "buildProject" }
# Export:
# Name: "${self:custom.serviceName}-buildProject"
# buildProjectRole:
# Description: 'buildProjectRole value'
# Value: { "Ref": "buildProjectRole" }
# Export:
# Name: "${self:custom.serviceName}-buildProjectRole"
# buildsTable:
# Description: 'buildsTable value'
# Value: { "Ref": "buildsTable" }
# Export:
# Name: "${self:custom.serviceName}-buildsTable"
# deploysTable:
# Description: 'deploysTable value'
# Value: { "Ref": "deploysTable" }
# Export:
# Name: "${self:custom.serviceName}-deploysTable"
# ecrRepository:
# Description: 'ecrRepository value'
# Value: { "Ref": "ecrRepository" }
# Export:
# Name: "${self:custom.serviceName}-ecrRepository"
# notifySlackRole:
# Description: 'notifySlackRole value'
# Value: { "Ref": "notifySlackRole" }
# Export:
# Name: "${self:custom.serviceName}-notifySlackRole"
# startBuildRole:
# Description: 'startBuildRole value'
# Value: { "Ref": "startBuildRole" }
# Export:
# Name: "${self:custom.serviceName}-startBuildRole"
# updateServiceRole:
# Description: 'updateServiceRole value'
# Value: { "Ref": "updateServiceRole" }
# Export:
# Name: "${self:custom.serviceName}-updateServiceRole"
# updateStackRole:
# Description: 'updateStackRole value'
# Value: { "Ref": "updateStackRole" }
# Export:
# Name: "${self:custom.serviceName}-updateStackRole"
# notifySnsTopic:
# Description: 'notifySnsTopic value'
# Value: { "Fn::Join" : ["", ["arn:aws:sns:${self:custom.region}:", { "Ref" : "AWS::AccountId" }, ":${self:custom.stackChangeTopic}" ] ] }
# Export:
# Name: "${self:custom.serviceName}-notifySnsTopic"