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
  #   DeletionPolicy: Retain
  #   UpdateReplacePolicy: Retain
  # 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: "*"

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

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

  # # This is the definition of how to build the code in the repository
  # CodeBuildProject:
  #   Type: AWS::CodeBuild::Project
  #   Properties:
  #     Artifacts:
  #       Type: CODEPIPELINE
  #     Source:
  #       Type: CODEPIPELINE
  #       BuildSpec: |
  #         version: 0.2
  #         phases:
  #           pre_build:
  #             commands:
  #               - aws ecr get-login-password --region $AWS_DEFAULT_REGION | docker login --username AWS --password-stdin $AWS_ACCOUNT_ID.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com
  #               - TAG=$(echo $CODEBUILD_RESOLVED_SOURCE_VERSION | head -c 8)
  #               - IMAGE_SERVER_URI=$REPO_SERVER_URI:$TAG
  #               - IMAGE_CACHE_URI=$REPO_CACHE_URI:$TAG
  #               - IMAGE_DB_URI=$REPO_DB_URI:$TAG
  #           build:
  #             commands:
  #               - docker build -t $IMAGE_SERVER_URI -f server.Dockerfile .
  #               - docker build -t $IMAGE_CACHE_URI -f cache.Dockerfile .
  #               - docker build -t $IMAGE_DB_URI -f database.Dockerfile .
  #           post_build:
  #             commands:
  #               - docker push $IMAGE_SERVER_URI
  #               - docker push $IMAGE_CACHE_URI
  #               - docker push $IMAGE_DB_URI
  #               - printf '{"ImageServerUri":"%s", "ImageCacheUri":"%s", "ImageDatabaseUri":"%s"}' $IMAGE_SERVER_URI $IMAGE_CACHE_URI $IMAGE_DB_URI > build.json
  #         artifacts:
  #           files: build.json
  #     Environment:
  #       ComputeType: BUILD_GENERAL1_SMALL
  #       Image: aws/codebuild/standard:5.0
  #       Type: LINUX_CONTAINER
  #       PrivilegedMode: true
  #     ServiceRole: !Ref CodeBuildServiceRole

  # A Webhook for the pipeline. Triggered manually only. This is done by
  # setting RegisterWithThirdParty to false. Also, PollForSourceChanges
  # needs to be set to false in the pipeline's source action config.
  PipelineWebhook: 
    Type: AWS::CodePipeline::Webhook
    Properties:
      AuthenticationConfiguration:
        SecretToken: !Ref GitHubToken
      Filters:
        - JsonPath: "$.ref"
          MatchEquals: refs/heads/{Branch}
      Authentication: GITHUB_HMAC
      TargetPipeline: !Ref Pipeline
      TargetAction: Source
      TargetPipelineVersion: !GetAtt Pipeline.Version
      RegisterWithThirdParty: false

  # This pipeline defines the steps to build, deploy, and release the application
  Pipeline:
    Type: AWS::CodePipeline::Pipeline
    Properties:
      RoleArn: !GetAtt CodePipelineServiceRole.Arn
      ArtifactStore:
        Type: S3
        Location: !Ref ArtifactBucket
      Stages:

        # First we have to pull the source code from the Github repository
        - Name: Source
          Actions:
            - Name: Source
              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: Source
              RunOrder: 1

        # Now we deploy the network resources: VPC, subnets, etc.
        - Name: Network
          Actions:
            - Name: Deploy
              ActionTypeId:
                Category: Deploy
                Owner: AWS
                Version: 1
                Provider: CloudFormation
              Configuration:
                ActionMode: CREATE_UPDATE
                RoleArn: !GetAtt CloudFormationDeployRole.Arn
                StackName: !Sub ${EnvironmentName}-Network
                TemplatePath: Source::deployments/network.yml
                Capabilities: CAPABILITY_IAM
                ParameterOverrides: !Sub |
                  {
                    "EnvironmentName": "${EnvironmentName}"
                  }
              InputArtifacts:
                - Name: Source
              OutputArtifacts:
                - Name: Network

        # Deploy the base resources: databases, the load balancer,
        # and the ECS/Fargate cluster
        - Name: BaseResources
          Actions:
            # Deploy the resources: Rabbit, Redis, and Postgres
            - Name: DeployResources
              ActionTypeId:
                Category: Deploy
                Owner: AWS
                Version: 1
                Provider: CloudFormation
              Configuration:
                ActionMode: CREATE_UPDATE
                RoleArn: !GetAtt CloudFormationDeployRole.Arn
                StackName: !Sub ${EnvironmentName}-Resources
                TemplatePath: Source::deployments/resources.yml
                Capabilities: CAPABILITY_IAM
                ParameterOverrides: !Sub |
                  {
                    "EnvironmentName": "${EnvironmentName}"
                  }
              InputArtifacts:
                - Name: Source
              OutputArtifacts:
                - Name: Resources

            # Deploy the application load balancer
            - Name: DeployLoadBalancer
              ActionTypeId:
                Category: Deploy
                Owner: AWS
                Version: 1
                Provider: CloudFormation
              Configuration:
                ActionMode: CREATE_UPDATE
                RoleArn: !GetAtt CloudFormationDeployRole.Arn
                StackName: !Sub ${EnvironmentName}-LoadBalancer
                TemplatePath: Source::deployments/alb.yml
                Capabilities: CAPABILITY_IAM
                ParameterOverrides: !Sub |
                  {
                    "EnvironmentName": "${EnvironmentName}"
                  }
              InputArtifacts:
                - Name: Source
              OutputArtifacts:
                - Name: LoadBalancer

            # Deploy the ECS/Fargate cluster
            - Name: DeployCluster
              ActionTypeId:
                Category: Deploy
                Owner: AWS
                Version: 1
                Provider: CloudFormation
              Configuration:
                ActionMode: CREATE_UPDATE
                RoleArn: !GetAtt CloudFormationDeployRole.Arn
                StackName: !Sub ${EnvironmentName}-Cluster
                TemplatePath: !Sub Source::deployments/cluster-${DeploymentType}.yml
                Capabilities: CAPABILITY_IAM
                ParameterOverrides: !Sub |
                  {
                    "EnvironmentName": "${EnvironmentName}"
                  }
              InputArtifacts:
                - Name: Source
              OutputArtifacts:
                - Name: Cluster

        # # Now we build the service images
        # - Name: ServiceImages
        #   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":"REPO_SERVER_URI",
        #               "value":"${ServerRepository.RepositoryUri}",
        #               "type":"PLAINTEXT"
        #             },
        #             {
        #               "name":"REPO_CACHE_URI",
        #               "value":"${CacheRepository.RepositoryUri}",
        #               "type":"PLAINTEXT"
        #             },
        #             {
        #               "name":"REPO_DB_URI",
        #               "value":"${DatabaseRepository.RepositoryUri}",
        #               "type":"PLAINTEXT"
        #             }
        #           ]
        #       InputArtifacts:
        #         - Name: Source
        #       OutputArtifacts:
        #         - Name: BuildOutput
        #       RunOrder: 1

        # # Finally we deploy the ECS/Fargate services to the cluster
        # - Name: Deploy
        #   Actions:
        #     # Deploy the server service
        #     - Name: DeployServer
        #       ActionTypeId:
        #         Category: Deploy
        #         Owner: AWS
        #         Version: 1
        #         Provider: CloudFormation
        #       Configuration:
        #         ActionMode: CREATE_UPDATE
        #         RoleArn: !GetAtt CloudFormationDeployRole.Arn
        #         StackName: !Sub ${EnvironmentName}-ServerService
        #         TemplatePath: !Sub Source::deployments/services-${DeploymentType}/server.yml
        #         Capabilities: CAPABILITY_IAM
        #         ParameterOverrides: !Sub |
        #           {
        #             "EnvironmentName": "${EnvironmentName}",
        #             "ImageUrl": {
        #               "Fn::GetParam" : ["BuildOutput", "build.json", "ImageServerUri"]
        #             }
        #           }
        #       InputArtifacts:
        #         - Name: Source
        #         - Name: BuildOutput

        #     # Deploy the cache service
        #     - Name: DeployCache
        #       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", "ImageCacheUri"]
        #             }
        #           }
        #       InputArtifacts:
        #         - Name: Source
        #         - Name: BuildOutput

        #     # Deploy the database service
        #     - Name: DeployDatabase
        #       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", "ImageDatabaseUri"]
        #             }
        #           }
        #       InputArtifacts:
        #         - Name: Source
        #         - Name: BuildOutput

Outputs:
  PipelineUrl:
    Value: !Sub https://console.aws.amazon.com/codepipeline/home?region=${AWS::Region}#/view/${Pipeline}