@bowtie/sls
Version:
Serverless helpers & utilities
882 lines (847 loc) • 36.8 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}-root
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: nodejs16.x
stage: dev
region: ${self:custom.region}
profile: ${opt:aws-profile, self:custom.service.aws.profile}
apiGateway:
restApiId:
Ref: ApiGatewayRestApi
restApiRootResourceId:
Fn::GetAtt: [ApiGatewayRestApi, RootResourceId]
# restApiResources:
# users: { 'Fn::ImportValue': 'postsapi-${opt:stage}-ApiGatewayResourceUsers' }
# users/me:
# Fn::ImportValue: 'postsapi-${opt:stage}-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://", { "Ref" : "ApiGatewayRestApi" }, ".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}
apiGatewayName: "${self:custom.namespace}-api"
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"
apiHandlerRoleName: "${self:custom.namespace}-api-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:
# email:
# handler: handler.sendEmail
# role: sendEmailRole
# events:
# - http:
# path: ${self:custom.apiBaseUrl}/email
# method: post
# cors: true
upload:
handler: handler.s3Upload
role: apiHandlerRole
events:
- http:
path: ${self:custom.apiBaseUrl}/upload
method: post
cors: true
# info:
# handler: handler.info
# role: apiHandlerRole
# events:
# - http:
# path: ${self:custom.apiBaseUrl}/info
# method: get
# cors: true
# verify-recaptcha:
# handler: handler.verifyRecaptcha
# events:
# - http:
# path: ${self:custom.apiBaseUrl}/recaptcha
# method: get
# cors: true
stack-change:
handler: handler.stackChange
role: notifySlackRole
events:
- sns: ${self:custom.stackChangeTopic}
build-change:
handler: handler.buildChange
role: updateStackRole
events:
- cloudwatchEvent:
description: 'CloudWatch Event triggered on a Code Build Project'
event:
source:
- "aws.codebuild"
detail-type:
- "CodeBuild Build State Change"
detail:
build-status:
- IN_PROGRESS
- SUCCEEDED
- FAILED
- STOPPED
project-name:
- ${self:custom.buildProjectName}
bitbucket-webhook:
handler: handler.bitbucketWebhook
role: startBuildRole
events:
- http:
path: bitbucket/webhook
method: post
github-webhook:
handler: handler.githubWebhook
role: startBuildRole
events:
- http:
path: github/webhook
method: post
# 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:
# Rest API
ApiGatewayRestApi:
Type: AWS::ApiGateway::RestApi
Properties:
Name: ${self:custom.apiGatewayName}
Description: "${self:custom.serviceName} API Gateway"
# TODO: Finish support for s3 static site? CloudFront? Or remove this?
siteBucket:
Type: 'AWS::S3::Bucket'
Properties:
BucketName: ${self:custom.siteBucketName}
# AccessControl: PublicRead
PublicAccessBlockConfiguration:
BlockPublicAcls: false
OwnershipControls:
Rules:
- ObjectOwnership: ObjectWriter
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
PublicAccessBlockConfiguration:
BlockPublicAcls: false
OwnershipControls:
Rules:
- ObjectOwnership: ObjectWriter
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}-*:*:*" ] ] }
apiHandlerRole:
Type: AWS::IAM::Role
Properties:
RoleName: ${self:custom.apiHandlerRoleName}
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Principal:
Service:
- lambda.amazonaws.com
Action: sts:AssumeRole
Policies:
- PolicyName: apiRolePolicy
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: "Allow"
Action:
- "s3:*"
Resource:
- 'arn:aws:s3:::${self:custom.secureBucketName}'
- 'arn:aws:s3:::${self:custom.secureBucketName}/*'
- 'arn:aws:s3:::${self:custom.assetBucketName}'
- 'arn:aws:s3:::${self:custom.assetBucketName}/*'
- Effect: "Allow"
Action:
- "dynamodb:DescribeTable"
Resource:
- { "Fn::Join" : ["", ["arn:aws:dynamodb:${self:custom.region}:", { "Ref" : "AWS::AccountId" }, ":table/${self:service}-*" ] ] }
- 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}-*:*:*" ] ] }
buildProject:
Type: AWS::CodeBuild::Project
DependsOn: buildProjectRole
Properties:
Name: ${self:custom.buildProjectName}
ServiceRole: ${self:custom.buildProjectRoleName}
Cache:
Type: S3
Location: ${self:custom.buildBucketName}
Artifacts:
Type: no_artifacts
Source:
# [HIGH] TODO: Dynamic source? GH vs BB
Location: ${self:custom.service.source.base}/${self:custom.service.source.repo}.git
Type: "${self:custom.service.source.type}"
Auth:
Type: OAUTH
Environment:
# [HIGH] TODO: Configure this
ComputeType: "BUILD_GENERAL1_SMALL"
PrivilegedMode: true
Image: "public.ecr.aws/bowtie/docker-builder"
Type: "LINUX_CONTAINER"
EnvironmentVariables:
- Name: CI
Value: 'true'
- Name: AWS_REGION
Value: ${self:custom.region}
- Name: AWS_ACCOUNT
Value: { "Ref" : "AWS::AccountId" }
- Name: REPO_SLUG
Value: ${self:custom.ecrRepoName}
- Name: AWS_BUCKET_NAME
Value: ${self:custom.buildBucketName}
- Name: AWS_SITE_BUCKET_NAME
Value: ${self:custom.siteBucketName}
- Name: IAM_ROLE_NAME
Value: ${self:custom.buildProjectRoleName}
- Name: GIT_REPO_NAME
Value: ${self:custom.service.source.repo, ""}
- Name: "${self:custom.service.source.type}_REPO"
Value: ${self:custom.service.source.repo, ""}
- Name: AIRBRAKE_PROJECT_ID
Value: ${self:custom.service.airbrake.id, ""}
- Name: AIRBRAKE_PROJECT_KEY
Value: ${self:custom.service.airbrake.key, ""}
buildProjectRole:
Type: AWS::IAM::Role
Properties:
RoleName: ${self:custom.buildProjectRoleName}
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: "Allow"
Principal:
Service:
- codebuild.amazonaws.com
Action: sts:AssumeRole
Policies:
- PolicyName: buildProject
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Action:
- dynamodb:*
Resource:
- { "Fn::Join" : ["", ["arn:aws:dynamodb:${self:custom.region}:", { "Ref" : "AWS::AccountId" }, ":table/*"]]}
# - { "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}"]]}
- Effect: "Allow"
Action:
- "s3:*"
Resource:
- 'arn:aws:s3:::${self:custom.buildBucketName}'
- 'arn:aws:s3:::${self:custom.buildBucketName}/*'
- 'arn:aws:s3:::${self:custom.siteBucketName}'
- 'arn:aws:s3:::${self:custom.siteBucketName}/*'
- Effect: "Allow"
Action:
- "ecr:InitiateLayerUpload"
- "ecr:UploadLayerPart"
- "ecr:CompleteLayerUpload"
- "ecr:GetDownloadUrlForLayer"
- "ecr:BatchGetImage"
- "ecr:BatchCheckLayerAvailability"
- "ecr:PutImage"
- "ecr:GetAuthorizationToken"
- "logs:CreateLogGroup"
- "logs:CreateLogStream"
- "logs:PutLogEvents"
- "cloudfront:ListInvalidations"
- "cloudfront:GetInvalidation"
- "cloudfront:CreateInvalidation"
# [HIGH] TODO: Lock down this resource definition
Resource: "*"
startBuildRole:
Type: AWS::IAM::Role
Properties:
RoleName: ${self:custom.startBuildRoleName}
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:
- Effect: "Allow"
Action:
- "ecr:BatchGetImage"
- "ecr:DescribeImages"
- "ecr:PutImage"
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}-*:*:*" ] ] }
- Effect: "Allow"
Action:
- "codebuild:StartBuild"
- "codebuild:BatchGetBuilds"
Resource:
- { "Fn::Join" : ["", ["arn:aws:codebuild:${self:custom.region}:", { "Ref" : "AWS::AccountId" }, ":project/${self:custom.buildProjectName}"]]}
- Effect: Allow
Action:
- dynamodb:*
Resource:
- { "Fn::Join" : ["", ["arn:aws:dynamodb:${self:custom.region}:", { "Ref" : "AWS::AccountId" }, ":table/*"]]}
# - { "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:
- "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:
- { "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}" ] ] }
notifySlackRole:
Type: AWS::IAM::Role
Properties:
RoleName: ${self:custom.notifySlackRoleName}
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:
- 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:
- "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:
- "ecs:ListTaskDefinitions"
- "cloudformation:DescribeStacks"
Resource: "*"
- Effect: "Allow"
Action:
- "SNS:Publish"
Resource:
- { "Fn::Join" : ["", ["arn:aws:sns:${self:custom.region}:", { "Ref" : "AWS::AccountId" }, ":${self:custom.stackChangeTopic}" ] ] }
- Effect: "Allow"
Action:
- "cloudwatch:DescribeAlarms"
- "cloudwatch:PutMetricAlarm"
Resource:
- { "Fn::Join" : ["", ["arn:aws:cloudwatch:${self:custom.region}:", { "Ref" : "AWS::AccountId" }, ":*/${self:custom.serviceName}*" ] ] }
- Effect: "Allow"
Action:
- "ecs:List*"
- "ecs:RunTask"
- "ecs:Describe*"
- "ecs:RegisterTaskDefinition"
- "ecs:DeregisterTaskDefinition"
- "ecs:UpdateService"
Resource:
- { "Fn::Join" : ["", ["arn:aws:ecs:${self:custom.region}:", { "Ref" : "AWS::AccountId" }, ":*/${self:custom.serviceName}*" ] ] }
- Effect: "Allow"
Action:
- "iam:AttachRolePolicy"
- "iam:CreateRole"
- "iam:GetPolicy"
- "iam:GetPolicyVersion"
- "iam:GetRole"
- "iam:PassRole"
- "iam:ListAttachedRolePolicies"
- "iam:ListRoles"
- "iam:ListGroups"
- "iam:ListUsers"
Resource:
- { "Fn::Join" : ["", ["arn:aws:iam::", { "Ref" : "AWS::AccountId" }, ":user/${self:custom.serviceName}*" ] ] }
- { "Fn::Join" : ["", ["arn:aws:iam::", { "Ref" : "AWS::AccountId" }, ":role/${self:custom.serviceName}*" ] ] }
- { "Fn::Join" : ["", ["arn:aws:iam::", { "Ref" : "AWS::AccountId" }, ":group/${self:custom.serviceName}*" ] ] }
- { "Fn::Join" : ["", ["arn:aws:iam::", { "Ref" : "AWS::AccountId" }, ":policy/${self:custom.serviceName}*" ] ] }
- { "Fn::Join" : ["", ["arn:aws:iam::", { "Ref" : "AWS::AccountId" }, ":instance-profile/${self:custom.serviceName}*" ] ] }
updateServiceRole:
Type: AWS::IAM::Role
Properties:
RoleName: ${self:custom.serviceUpdateRoleName}
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Principal:
Service:
- cloudformation.amazonaws.com
Action: sts:AssumeRole
Policies:
- PolicyName: updateService
PolicyDocument:
Version: '2012-10-17'
Statement:
- 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:
- "SNS:Publish"
Resource:
- { "Fn::Join" : ["", ["arn:aws:sns:${self:custom.region}:", { "Ref" : "AWS::AccountId" }, ":${self:custom.stackChangeTopic}" ] ] }
- Effect: "Allow"
Action:
- "application-autoscaling:Describe*"
- "application-autoscaling:PutScalingPolicy"
- "application-autoscaling:DeleteScalingPolicy"
- "application-autoscaling:RegisterScalableTarget"
- "cloudwatch:DescribeAlarms"
- "cloudwatch:PutMetricAlarm"
- "ecs:List*"
- "ecs:Describe*"
- "ecs:RegisterTaskDefinition"
- "ecs:DeregisterTaskDefinition"
- "ecs:UpdateService"
- "iam:AttachRolePolicy"
- "iam:CreateRole"
- "iam:GetPolicy"
- "iam:GetPolicyVersion"
- "iam:GetRole"
- "iam:PassRole"
- "iam:ListAttachedRolePolicies"
- "iam:ListRoles"
- "iam:ListGroups"
- "iam:ListUsers"
Resource: "*"
updateStackRole:
Type: AWS::IAM::Role
Properties:
RoleName: ${self:custom.stackUpdateRoleName}
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Principal:
Service:
- lambda.amazonaws.com
Action: sts:AssumeRole
Policies:
- PolicyName: updateStack
PolicyDocument:
Version: '2012-10-17'
Statement:
- 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:
- "ecr:BatchGetImage"
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}-*:*:*" ] ] }
- 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}" ] ] }
- Effect: "Allow"
Action:
- "ecr:BatchGetImage"
- "ecr:DescribeImages"
Resource: "*"
Outputs:
# RestApi resource ID (e.g. ei829oe)
restApiId:
Value:
Ref: ApiGatewayRestApi
Export:
Name: "${self:custom.serviceName}-restApiId"
# RestApi Root Resource (the implicit '/' path)
restApiRootResourceId:
Value:
Fn::GetAtt: [ApiGatewayRestApi, RootResourceId]
Export:
Name: "${self:custom.serviceName}-restApiRootResourceId"
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"