UNPKG

@bowtie/sls

Version:

Serverless helpers & utilities

1,171 lines (1,111 loc) 44.4 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} plugins: - serverless-offline # 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} # [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/${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" apiHandlerRoleName: "${self:custom.namespace}-api-handler-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 # builds-tags: # handler: handler.builds_tags # role: apiHandlerRole # events: # - http: # path: ${self:custom.apiBaseUrl}/builds/tags # method: get # cors: true # deploys-stacks: # handler: handler.deploys_stacks # role: apiHandlerRole # events: # - http: # path: ${self:custom.apiBaseUrl}/deploys/stacks # method: get # cors: true builds-deploy: handler: handler.builds_deploy role: apiHandlerRole events: - http: path: ${self:custom.apiBaseUrl}/builds/{id}/deploy/{stack} method: get cors: true request: parameters: paths: id: true stack: true builds-index: handler: handler.builds_index role: apiHandlerRole events: - http: path: ${self:custom.apiBaseUrl}/builds method: get cors: true builds-show: handler: handler.builds_show role: apiHandlerRole events: - http: path: ${self:custom.apiBaseUrl}/builds/{id} method: get cors: true request: parameters: paths: id: true # builds-logs: # handler: handler.builds_logs # role: apiHandlerRole # events: # - http: # path: ${self:custom.apiBaseUrl}/builds/{id}/logs # method: get # cors: true # request: # parameters: # paths: # id: true # builds-create: # handler: handler.builds_create # role: apiHandlerRole # events: # - http: # path: ${self:custom.apiBaseUrl}/builds # method: post # cors: true builds-update: handler: handler.builds_update role: apiHandlerRole events: - http: path: ${self:custom.apiBaseUrl}/builds/{id} method: put cors: true request: parameters: paths: id: true # builds-destroy: # handler: handler.builds_destroy # role: apiHandlerRole # events: # - http: # path: ${self:custom.apiBaseUrl}/builds/{id} # method: delete # cors: true # request: # parameters: # paths: # id: true deploys-index: handler: handler.deploys_index role: apiHandlerRole events: - http: path: ${self:custom.apiBaseUrl}/deploys method: get cors: true deploys-show: handler: handler.deploys_show role: apiHandlerRole events: - http: path: ${self:custom.apiBaseUrl}/deploys/{id} method: get cors: true request: parameters: paths: id: true # deploys-create: # handler: handler.deploys_create # role: apiHandlerRole # events: # - http: # path: ${self:custom.apiBaseUrl}/deploys # method: post # cors: true deploys-update: handler: handler.deploys_update role: apiHandlerRole events: - http: path: ${self:custom.apiBaseUrl}/deploys/{id} method: put cors: true request: parameters: paths: id: true # deploys-destroy: # handler: handler.deploys_destroy # role: apiHandlerRole # events: # - http: # path: ${self:custom.apiBaseUrl}/deploys/{id} # method: delete # cors: true # request: # parameters: # paths: # id: true verify-recaptcha: handler: handler.verifyRecaptcha events: - http: path: ${self:custom.apiBaseUrl}/recaptcha method: get cors: true submissions-index: handler: handler.submissions_index role: apiHandlerRole events: - http: path: ${self:custom.apiBaseUrl}/submissions method: get cors: true submissions-show: handler: handler.submissions_show role: apiHandlerRole events: - http: path: ${self:custom.apiBaseUrl}/submissions/{id} method: get cors: true request: parameters: paths: id: true submissions-create: handler: handler.submissions_create role: apiHandlerRole events: - http: path: ${self:custom.apiBaseUrl}/submissions method: post cors: true submissions-update: handler: handler.submissions_update role: apiHandlerRole events: - http: path: ${self:custom.apiBaseUrl}/submissions/{id} method: put cors: true request: parameters: paths: id: true submissions-download: handler: handler.submissions_download role: apiHandlerRole events: - http: path: ${self:custom.apiBaseUrl}/submissions/{id}/download method: get cors: true request: parameters: paths: id: true documents-index: handler: handler.documents_index role: apiHandlerRole events: - http: path: ${self:custom.apiBaseUrl}/documents method: get cors: true documents-audits: handler: handler.documents_audits role: apiHandlerRole 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: apiHandlerRole 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: apiHandlerRole events: - http: path: ${self:custom.apiBaseUrl}/documents/{id} method: get cors: true request: parameters: paths: id: true documents-create: handler: handler.documents_create role: apiHandlerRole events: - http: path: ${self:custom.apiBaseUrl}/documents method: post cors: true documents-update: handler: handler.documents_update role: apiHandlerRole events: - http: path: ${self:custom.apiBaseUrl}/documents/{id} method: put cors: true request: parameters: paths: id: true documents-destroy: handler: handler.documents_destroy role: apiHandlerRole events: - http: path: ${self:custom.apiBaseUrl}/documents/{id} method: delete cors: true request: parameters: paths: id: 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: # 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}-*:*:*" ] ] } 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: 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}" ] ] } 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/${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/${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: 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"