Parameters:
  GitHubRepo:
    Type: String
  GitHubBranch:
    Type: String
  GitHubToken:
    Type: String
    NoEcho: true
  GitHubUser:
    Type: String
  EnvironmentName:
    Type: String
    Default: production
  DeploymentType:
    Type: String
    Default: fargate
    AllowedValues: [ecs, fargate]

Resources:
  # Create ECR respositories to hold built docker images
  ServerRepository:
    Type: AWS::ECR::Repository
    Properties:
      RepositoryName: !Sub ${EnvironmentName}/server
  # CacheRepository:
  #   Type: AWS::ECR::Repository
  #   DeletionPolicy: Retain
  #   UpdateReplacePolicy: Retain
  # DatabaseRepository:
  #   Type: AWS::ECR::Repository
  #   DeletionPolicy: Retain
  #   UpdateReplacePolicy: Retain

  # A role used to give CodeBuild permission to access code,
  # build it, and upload the build results to ECR
  CodeBuildServiceRole:
    Type: AWS::IAM::Role
    Properties:
      Path: /
      AssumeRolePolicyDocument:
        Version: 2012-10-17
        Statement:
          - Effect: Allow
            Principal:
              Service: codebuild.amazonaws.com
            Action: sts:AssumeRole
      Policies:
        - PolicyName: root
          PolicyDocument:
            Version: 2012-10-17
            Statement:
              - Resource: "*"
                Effect: Allow
                Action:
                  - logs:CreateLogGroup
                  - logs:CreateLogStream
                  - logs:PutLogEvents
                  - ecr:GetAuthorizationToken
              - Resource: !Sub arn:aws:s3:::${ArtifactBucket}/*
                Effect: Allow
                Action:
                  - s3:GetObject
                  - s3:PutObject
                  - s3:GetObjectVersion
              - Resource:
                  - !Sub arn:aws:ecr:${AWS::Region}:${AWS::AccountId}:repository/${ServerRepository}
                  # - !Sub arn:aws:ecr:${AWS::Region}:${AWS::AccountId}:repository/${CacheRepository}
                  # - !Sub arn:aws:ecr:${AWS::Region}:${AWS::AccountId}:repository/${DatabaseRepository}
                Effect: Allow
                Action:
                  - ecr:GetDownloadUrlForLayer
                  - ecr:BatchGetImage
                  - ecr:BatchCheckLayerAvailability
                  - ecr:PutImage
                  - ecr:InitiateLayerUpload
                  - ecr:UploadLayerPart
                  - ecr:CompleteLayerUpload

  # Role used to give CodePipeline to release a build.
  CodePipelineServiceRole:
    Type: AWS::IAM::Role
    Properties:
      Path: /
      AssumeRolePolicyDocument:
        Version: 2012-10-17
        Statement:
          - Effect: Allow
            Principal:
              Service: codepipeline.amazonaws.com
            Action: sts:AssumeRole
      Policies:
        - PolicyName: root
          PolicyDocument:
            Version: 2012-10-17
            Statement:
              # Allow codepipeline to put artifacts in the S3 bucket
              # as well as get artifacts back out of it.
              - Resource:
                  - !Sub arn:aws:s3:::${ArtifactBucket}/*
                Effect: Allow
                Action:
                  - s3:PutObject
                  - s3:GetObject
                  - s3:GetObjectVersion
                  - s3:GetBucketVersioning
              # Allow codepipeline to build code builds
              - Resource: "*"
                Effect: Allow
                Action:
                  - codebuild:StartBuild
                  - codebuild:BatchGetBuilds
                  - iam:PassRole
              # Allow codepipeline to deploy cloudformation stacks
              - Effect: Allow
                Action:
                  - cloudformation:CreateChangeSet
                  - cloudformation:CreateStack
                  - cloudformation:CreateUploadBucket
                  - cloudformation:DeleteStack
                  - cloudformation:Describe*
                  - cloudformation:List*
                  - cloudformation:UpdateStack
                  - cloudformation:ValidateTemplate
                  - cloudformation:ExecuteChangeSet
                Resource: "*"
              # Allow codepipeline to get images from ECR.
              # Actions from AmazonEC2ContainerRegistryReadOnly.
              - Effect: Allow
                Action:
                  - ecr:GetAuthorizationToken
                  - ecr:BatchCheckLayerAvailability
                  - ecr:GetDownloadUrlForLayer
                  - ecr:GetRepositoryPolicy
                  - ecr:DescribeRepositories
                  - ecr:ListImages
                  - ecr:DescribeImages
                  - ecr:BatchGetImage
                  - ecr:GetLifecyclePolicy
                  - ecr:GetLifecyclePolicyPreview
                  - ecr:ListTagsForResource
                  - ecr:DescribeImageScanFindings
                Resource: "*"

  # CloudFormation deployment role. This role is passed by CodeBuild to
  # CloudFormation to use when setting up the application resources
  CloudFormationDeployRole:
    Type: AWS::IAM::Role
    Properties:
      Path: /
      AssumeRolePolicyDocument:
        Version: 2012-10-17
        Statement:
          - Effect: Allow
            Principal:
              Service: cloudformation.amazonaws.com
            Action: sts:AssumeRole
      Policies:
        - PolicyName: deploy-stack
          PolicyDocument:
            Statement:
              - Effect: Allow
                Action:
                  - "iam:*"
                  - "ec2:*"
                  - "ecs:*"
                  - "elasticloadbalancing:*"
                  - "autoscaling:*"
                  - "elasticache:*"
                  - "logs:*"
                  - "application-autoscaling:*"
                  - "cloudwatch:*"
                  - "route53:*"
                  - "rds:*"
                  - "mq:*"
                  # - "secretsmanager:*"
                  - "ssm:*"
                Resource: "*"

  # CloudWatch event role. This role allows an event to start our pipeline.
  CloudWatchEventRole:
    Type: AWS::IAM::Role
    Properties:
      Path: /
      AssumeRolePolicyDocument:
        Version: 2012-10-17
        Statement:
          - Effect: Allow
            Principal:
              Service: events.amazonaws.com
            Action: sts:AssumeRole
      Policies:
        - PolicyName: cwe-pipeline-execution
          PolicyDocument:
            Version: 2012-10-17
            Statement:
              - Effect: Allow
                Action: codepipeline:StartPipelineExecution
                Resource: !Sub arn:aws:codepipeline:${AWS::Region}:${AWS::AccountId}:${Pipeline}

  # While the build is in progress we need a place to store artifacts
  ArtifactBucket:
    Type: AWS::S3::Bucket
    Properties:
      BucketName: !Sub microservices-services-${AWS::AccountId}

  # GitHub source credentials 
  SourceCreds:
    Type: AWS::CodeBuild::SourceCredential
    Properties:
      Token: !Ref GitHubToken
      ServerType: GITHUB
      AuthType: PERSONAL_ACCESS_TOKEN

  # Build a service
  CodeBuildProject:
    Type: AWS::CodeBuild::Project
    DependsOn: SourceCreds
    Properties:
      Artifacts:
        Type: NO_ARTIFACTS
      Source:
        Type: GITHUB
        Location: !Sub https://github.com/${GitHubUser}/${GitHubRepo}.git
        BuildSpec: deployments/buildspec/service.yml
      SourceVersion: !Ref GitHubBranch
      Environment:
        ComputeType: BUILD_GENERAL1_SMALL
        Image: aws/codebuild/standard:5.0
        Type: LINUX_CONTAINER
        PrivilegedMode: true
        EnvironmentVariables:
          - Name: AWS_ACCOUNT_ID
            Value: !Sub ${AWS::AccountId}
          - Name: SERVICE
            Value: server
          - Name: REPO_URI
            Value: !Sub ${ServerRepository.RepositoryUri}
      Triggers:
        Webhook: true
        FilterGroups:
        - - Type: EVENT
            Pattern: PUSH
            ExcludeMatchedPattern: false
          - Type: HEAD_REF
            Pattern: !Sub ^refs/heads/${GitHubBranch}$
            ExcludeMatchedPattern: false
          - Type: FILE_PATH
            Pattern: ^(cmd/server/|internal/|web/|server.Dockerfile)
            ExcludeMatchedPattern: false
        - - Type: EVENT
            Pattern: PUSH
            ExcludeMatchedPattern: false
          - Type: HEAD_REF
            Pattern: !Sub ^refs/heads/${GitHubBranch}$
            ExcludeMatchedPattern: false
          - Type: COMMIT_MESSAGE
            Pattern: \[(BuildServer|BuildAll)\]
            ExcludeMatchedPattern: false
      ServiceRole: !Ref CodeBuildServiceRole
      TimeoutInMinutes: 10

  # A Webhook for the pipeline
  PipelineWebhook: 
    Type: AWS::CodePipeline::Webhook
    Properties:
      AuthenticationConfiguration:
        SecretToken: !Ref GitHubToken
      Filters:
        - JsonPath: "$.ref"
          MatchEquals: refs/heads/{Branch}
      Authentication: GITHUB_HMAC
      TargetPipeline: !Ref Pipeline
      TargetAction: Code
      TargetPipelineVersion: !GetAtt Pipeline.Version
      RegisterWithThirdParty: false

  # Pipeline
  # Also need a CloudWatch event:
  # https://docs.aws.amazon.com/codepipeline/latest/userguide/create-cwe-ecr-source-cfn.html
  Pipeline:
    Type: AWS::CodePipeline::Pipeline
    Properties:
      RoleArn: !GetAtt CodePipelineServiceRole.Arn
      ArtifactStore:
        Type: S3
        Location: !Ref ArtifactBucket
      Stages:
        # Get source artifacts
        - Name: Source
          Actions:
            # Get an image from an ECR repository
            - Name: Source
              ActionTypeId:
                Category: Source
                Owner: AWS
                Version: 1
                Provider: ECR
              Configuration:
                RepositoryName: !Ref ServerRepository
                ImageTag: latest
              OutputArtifacts:
                - Name: Source
              RunOrder: 1
            # Get source from a GitHub repository
            - Name: Code
              ActionTypeId:
                Category: Source
                Owner: ThirdParty
                Version: 1
                Provider: GitHub
              Configuration:
                Owner: !Ref GitHubUser
                Repo: !Ref GitHubRepo
                Branch: !Ref GitHubBranch
                OAuthToken: !Ref GitHubToken
                PollForSourceChanges: false
              OutputArtifacts:
                - Name: Code
              RunOrder: 1
        # Deploy the service to the ECS/Fargate cluster
        - Name: Deploy
          Actions:
            - Name: Deploy
              ActionTypeId:
                Category: Deploy
                Owner: AWS
                Version: 1
                Provider: CloudFormation
              Configuration:
                ActionMode: CREATE_UPDATE
                RoleArn: !GetAtt CloudFormationDeployRole.Arn
                StackName: !Sub ${EnvironmentName}-ServerService
                TemplatePath: !Sub Code::deployments/services-${DeploymentType}/server.yml
                Capabilities: CAPABILITY_IAM
                ParameterOverrides: !Sub |
                  {
                    "EnvironmentName": "${EnvironmentName}",
                    "ImageUrl": "${ServerRepository.RepositoryUri}:latest"
                  }
              InputArtifacts:
                - Name: Source
                - Name: Code

  # A CloudWatch event that will trigger out pipeline when an image is pushed
  # to the specified ECR repository
  CloudWatchEventRule:
    Type: AWS::Events::Rule
    Properties:
      EventPattern:
        detail:
          action-type: [PUSH]
          image-tag: [latest]
          repository-name: [!Ref ServerRepository]
          result: [SUCCESS]
        detail-type: [ECR Image Action]
        source: [aws.ecr]
      Targets:
        - Arn: !Sub arn:aws:codepipeline:${AWS::Region}:${AWS::AccountId}:${Pipeline}
          RoleArn: !GetAtt CloudWatchEventRole.Arn
          Id: codepipeline-Pipeline

  # # The cache pipeline
  # PipelineCache:
  #   Type: AWS::CodePipeline::Pipeline
  #   Properties:
  #     RoleArn: !GetAtt CodePipelineServiceRole.Arn
  #     ArtifactStore:
  #       Type: S3
  #       Location: !Ref ArtifactBucket
  #     Stages:
  #       # Pull the source code from the Github repository
  #       - Name: Source
  #         Actions:
  #           - Name: Source
  #             Namespace: SourceVariables
  #             ActionTypeId:
  #               Category: Source
  #               Owner: ThirdParty
  #               Version: 1
  #               Provider: GitHub
  #             Configuration:
  #               Owner: !Ref GitHubUser
  #               Repo: !Ref GitHubRepo
  #               Branch: !Ref GitHubBranch
  #               OAuthToken: !Ref GitHubToken
  #             OutputArtifacts:
  #               - Name: Source
  #             RunOrder: 1
  #       # Build a service image
  #       - Name: Build
  #         Actions:
  #           - Name: Build
  #             ActionTypeId:
  #               Category: Build
  #               Owner: AWS
  #               Version: 1
  #               Provider: CodeBuild
  #             Configuration:
  #               ProjectName: !Ref CodeBuildProject
  #               EnvironmentVariables: !Sub |
  #                 [
  #                   {
  #                     "name":"AWS_DEFAULT_REGION",
  #                     "value":"${AWS::Region}",
  #                     "type":"PLAINTEXT"
  #                   },
  #                   {
  #                     "name":"AWS_ACCOUNT_ID",
  #                     "value":"${AWS::AccountId}",
  #                     "type":"PLAINTEXT"
  #                   },
  #                   {
  #                     "name":"SERVICE",
  #                     "value":"cache",
  #                     "type":"PLAINTEXT"
  #                   },
  #                   {
  #                     "name":"REPO_URI",
  #                     "value":"${CacheRepository.RepositoryUri}",
  #                     "type":"PLAINTEXT"
  #                   }
  #                 ]
  #             InputArtifacts:
  #               - Name: Source
  #             OutputArtifacts:
  #               - Name: BuildOutput
  #             RunOrder: 1
  #       # Deploy the service to the ECS/Fargate cluster
  #       - Name: Deploy
  #         Actions:
  #           - Name: Deploy
  #             ActionTypeId:
  #               Category: Deploy
  #               Owner: AWS
  #               Version: 1
  #               Provider: CloudFormation
  #             Configuration:
  #               ActionMode: CREATE_UPDATE
  #               RoleArn: !GetAtt CloudFormationDeployRole.Arn
  #               StackName: !Sub ${EnvironmentName}-CacheService
  #               TemplatePath: !Sub Source::deployments/services-${DeploymentType}/cache.yml
  #               Capabilities: CAPABILITY_IAM
  #               ParameterOverrides: !Sub |
  #                 {
  #                   "EnvironmentName": "${EnvironmentName}",
  #                   "ImageUrl": {
  #                     "Fn::GetParam" : ["BuildOutput", "build.json", "ImageUri"]
  #                   }
  #                 }
  #             InputArtifacts:
  #               - Name: Source
  #               - Name: BuildOutput

  # # The database pipeline
  # PipelineDatabase:
  #   Type: AWS::CodePipeline::Pipeline
  #   Properties:
  #     RoleArn: !GetAtt CodePipelineServiceRole.Arn
  #     ArtifactStore:
  #       Type: S3
  #       Location: !Ref ArtifactBucket
  #     Stages:
  #       # Pull the source code from the Github repository
  #       - Name: Source
  #         Actions:
  #           - Name: Source
  #             Namespace: SourceVariables
  #             ActionTypeId:
  #               Category: Source
  #               Owner: ThirdParty
  #               Version: 1
  #               Provider: GitHub
  #             Configuration:
  #               Owner: !Ref GitHubUser
  #               Repo: !Ref GitHubRepo
  #               Branch: !Ref GitHubBranch
  #               OAuthToken: !Ref GitHubToken
  #             OutputArtifacts:
  #               - Name: Source
  #             RunOrder: 1
  #       # Build a service image
  #       - Name: Build
  #         Actions:
  #           - Name: Build
  #             ActionTypeId:
  #               Category: Build
  #               Owner: AWS
  #               Version: 1
  #               Provider: CodeBuild
  #             Configuration:
  #               ProjectName: !Ref CodeBuildProject
  #               EnvironmentVariables: !Sub |
  #                 [
  #                   {
  #                     "name":"AWS_DEFAULT_REGION",
  #                     "value":"${AWS::Region}",
  #                     "type":"PLAINTEXT"
  #                   },
  #                   {
  #                     "name":"AWS_ACCOUNT_ID",
  #                     "value":"${AWS::AccountId}",
  #                     "type":"PLAINTEXT"
  #                   },
  #                   {
  #                     "name":"SERVICE",
  #                     "value":"database",
  #                     "type":"PLAINTEXT"
  #                   },
  #                   {
  #                     "name":"REPO_URI",
  #                     "value":"${DatabaseRepository.RepositoryUri}",
  #                     "type":"PLAINTEXT"
  #                   }
  #                 ]
  #             InputArtifacts:
  #               - Name: Source
  #             OutputArtifacts:
  #               - Name: BuildOutput
  #             RunOrder: 1
  #       # Deploy the service to the ECS/Fargate cluster
  #       - Name: Deploy
  #         Actions:
  #           - Name: Deploy
  #             ActionTypeId:
  #               Category: Deploy
  #               Owner: AWS
  #               Version: 1
  #               Provider: CloudFormation
  #             Configuration:
  #               ActionMode: CREATE_UPDATE
  #               RoleArn: !GetAtt CloudFormationDeployRole.Arn
  #               StackName: !Sub ${EnvironmentName}-DatabaseService
  #               TemplatePath: !Sub Source::deployments/services-${DeploymentType}/database.yml
  #               Capabilities: CAPABILITY_IAM
  #               ParameterOverrides: !Sub |
  #                 {
  #                   "EnvironmentName": "${EnvironmentName}",
  #                   "ImageUrl": {
  #                     "Fn::GetParam" : ["BuildOutput", "build.json", "ImageUri"]
  #                   }
  #                 }
  #             InputArtifacts:
  #               - Name: Source
  #               - Name: BuildOutput

# Outputs:
#   PipelineServerUrl:
#     Value: !Sub https://console.aws.amazon.com/codepipeline/home?region=${AWS::Region}#/view/${PipelineServer}
#   PipelineCacheUrl:
#     Value: !Sub https://console.aws.amazon.com/codepipeline/home?region=${AWS::Region}#/view/${PipelineCache}
#   PipelineDatabaseUrl:
#     Value: !Sub https://console.aws.amazon.com/codepipeline/home?region=${AWS::Region}#/view/${PipelineDatabase}