# 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"
