UNPKG

@bowtie/sls

Version:

Serverless helpers & utilities

527 lines (498 loc) 20.1 kB
# 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"