UNPKG

@bowtie/sls

Version:

Serverless helpers & utilities

882 lines (847 loc) 36.8 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}-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"