@bowtie/sls
Version:
Serverless helpers & utilities
405 lines (384 loc) • 15.4 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}-ci
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/${env:SERVICE_NAME}.yml):${env:SERVICE_NAME}}
apiVersion: v1
apiBaseUrl: "api/${self:custom.apiVersion}"
region: ${opt:region, self:custom.service.aws.region}
namespace: "${self:service}-${self:custom.region}"
serviceName: ${env:SERVICE_NAME}
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"
apiCiRoleName: "${self:custom.namespace}-api-ci-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:
info:
handler: handler.info
role: apiCiRole
events:
- http:
path: ${self:custom.apiBaseUrl}/info
method: get
cors: true
builds-index:
handler: handler.builds_index
role: apiCiRole
events:
- http:
path: ${self:custom.apiBaseUrl}/builds
method: get
cors: true
builds-tags:
handler: handler.builds_tags
role: apiCiRole
events:
- http:
path: ${self:custom.apiBaseUrl}/builds/tags
method: get
cors: true
builds-show:
handler: handler.builds_show
role: apiCiRole
events:
- http:
path: ${self:custom.apiBaseUrl}/builds/{id}
method: get
cors: true
request:
parameters:
paths:
id: true
# builds-create:
# handler: handler.builds_create
# role: apiCiRole
# events:
# - http:
# path: ${self:custom.apiBaseUrl}/builds
# method: post
# cors: true
# builds-update:
# handler: handler.builds_update
# role: apiCiRole
# events:
# - http:
# path: ${self:custom.apiBaseUrl}/builds/{id}
# method: put
# cors: true
# request:
# parameters:
# paths:
# id: true
# builds-destroy:
# handler: handler.builds_destroy
# role: apiCiRole
# events:
# - http:
# path: ${self:custom.apiBaseUrl}/builds/{id}
# method: delete
# cors: true
# request:
# parameters:
# paths:
# id: true
deploys-index:
handler: handler.deploys_index
role: apiCiRole
events:
- http:
path: ${self:custom.apiBaseUrl}/deploys
method: get
cors: true
deploys-stacks:
handler: handler.deploys_stacks
role: apiCiRole
events:
- http:
path: ${self:custom.apiBaseUrl}/deploys/stacks
method: get
cors: true
deploys-show:
handler: handler.deploys_show
role: apiCiRole
events:
- http:
path: ${self:custom.apiBaseUrl}/deploys/{id}
method: get
cors: true
request:
parameters:
paths:
id: true
deploys-create:
handler: handler.deploys_create
role: apiCiRole
events:
- http:
path: ${self:custom.apiBaseUrl}/deploys
method: post
cors: true
deploys-update:
handler: handler.deploys_update
role: apiCiRole
events:
- http:
path: ${self:custom.apiBaseUrl}/deploys/{id}
method: put
cors: true
request:
parameters:
paths:
id: true
deploys-destroy:
handler: handler.deploys_destroy
role: apiCiRole
events:
- http:
path: ${self:custom.apiBaseUrl}/deploys/{id}
method: delete
cors: true
request:
parameters:
paths:
id: true
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:
apiCiRole:
Type: AWS::IAM::Role
Properties:
RoleName: ${self:custom.apiCiRoleName}
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Principal:
Service:
- lambda.amazonaws.com
Action: sts:AssumeRole
Policies:
- PolicyName: slsApiPolicy
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"