Parameters:
  GitHubRepo:
    Type: String
  GitHubBranch:
    Type: String
  GitHubToken:
    Type: String
    NoEcho: true
  GitHubUser:
    Type: String
  EnvironmentName:
    Type: String
    Default: production
  LaunchType:
    Type: String
    Default: Fargate
    AllowedValues: [Fargate, EC2]

# Conditions:
#   Fargate: !Equals [ !Ref LaunchType, 'Fargate' ]

Resources:

  #-----------------------------------------------------------------------------#
  # Artifact Bucket
  #-----------------------------------------------------------------------------#
  ArtifactBucket:
    Type: AWS::S3::Bucket
    Properties:
      BucketName: !Sub microservices-infrastructure-${AWS::AccountId}
      VersioningConfiguration: 
        Status: Enabled

  #-----------------------------------------------------------------------------#
  # Source Credentials (for CodeBuild)
  #-----------------------------------------------------------------------------#
  SourceCredentials:
    Type: AWS::CodeBuild::SourceCredential
    Properties:
      Token: !Ref GitHubToken
      ServerType: GITHUB
      AuthType: PERSONAL_ACCESS_TOKEN

  #-----------------------------------------------------------------------------#
  # CodePipeline
  #-----------------------------------------------------------------------------#
  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 # only manual action

  Pipeline:
    Type: AWS::CodePipeline::Pipeline
    # DependsOn:
      # - SSMArtifactBucket
      # - SSMCodePipelineServiceRoleArn
    Properties:
      RoleArn: !GetAtt CodePipelineServiceRole.Arn
      ArtifactStore:
        Type: S3
        Location: !Ref ArtifactBucket
      Stages:

        #-----------------------------------------------------------------------------#
        # Source
        #-----------------------------------------------------------------------------#
        - 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
                PollForSourceChanges: false
              OutputArtifacts:
                - Name: Source
              RunOrder: 1

        #-----------------------------------------------------------------------------#
        # Infrastructure Resources
        #-----------------------------------------------------------------------------#
        - Name: Network_Resources
          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

        - Name: Base_Resources
          Actions:
            # Rabbit, Redis, and Postgres
            - Name: Resources
              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

            # Application load balancer
            - Name: Load_Balancer
              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

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

        # #-----------------------------------------------------------------------------#
        # # Services
        # #-----------------------------------------------------------------------------#
        # - Name: Services
        #   Actions:

        #     - Name: Server
        #       ActionTypeId:
        #         Category: Deploy
        #         Owner: AWS
        #         Version: 1
        #         Provider: CloudFormation
        #       Configuration:
        #         ActionMode: CREATE_UPDATE
        #         RoleArn: !GetAtt CloudFormationDeployRole.Arn
        #         StackName: !Sub ${EnvironmentName}-Server-Service
        #         TemplatePath: Source::deployments/services/server.yml
        #         Capabilities: CAPABILITY_IAM
        #         ParameterOverrides: !Sub
        #           - |
        #             {
        #               "ServiceName": "server",
        #               "EnvironmentName": "${EnvironmentName}",
        #               "LaunchType": "${LaunchType}",
        #               "ImageUrl": "amazon/amazon-ecs-sample",
        #               "ContainerMemory": ${memory}
        #             }
        #           - memory: !If [ Fargate, 512, 230 ]
        #       InputArtifacts:
        #         - Name: Source

        #     - Name: Cache
        #       ActionTypeId:
        #         Category: Deploy
        #         Owner: AWS
        #         Version: 1
        #         Provider: CloudFormation
        #       Configuration:
        #         ActionMode: CREATE_UPDATE
        #         RoleArn: !GetAtt CloudFormationDeployRole.Arn
        #         StackName: !Sub ${EnvironmentName}-Cache-Service
        #         TemplatePath: Source::deployments/services/cache.yml
        #         Capabilities: CAPABILITY_IAM
        #         ParameterOverrides: !Sub
        #           - |
        #             {
        #               "ServiceName": "cache",
        #               "EnvironmentName": "${EnvironmentName}",
        #               "LaunchType": "${LaunchType}",
        #               "ImageUrl": "amazon/amazon-ecs-sample",
        #               "ContainerMemory": ${memory}
        #             }
        #           - memory: !If [ Fargate, 512, 230 ]
        #       InputArtifacts:
        #         - Name: Source

        #     - Name: Database
        #       ActionTypeId:
        #         Category: Deploy
        #         Owner: AWS
        #         Version: 1
        #         Provider: CloudFormation
        #       Configuration:
        #         ActionMode: CREATE_UPDATE
        #         RoleArn: !GetAtt CloudFormationDeployRole.Arn
        #         StackName: !Sub ${EnvironmentName}-Database-Service
        #         TemplatePath: Source::deployments/services/database.yml
        #         Capabilities: CAPABILITY_IAM
        #         ParameterOverrides: !Sub
        #           - |
        #             {
        #               "ServiceName": "database",
        #               "EnvironmentName": "${EnvironmentName}",
        #               "LaunchType": "${LaunchType}",
        #               "ImageUrl": "amazon/amazon-ecs-sample",
        #               "ContainerMemory": ${memory}
        #             }
        #           - memory: !If [ Fargate, 512, 230 ]
        #       InputArtifacts:
        #         - Name: Source


        # #-----------------------------------------------------------------------------#
        # # Service Pipelines
        # #-----------------------------------------------------------------------------#
        # - Name: Service_Pipelines
        #   Actions:

        #     - Name: Server
        #       ActionTypeId:
        #         Category: Deploy
        #         Owner: AWS
        #         Version: 1
        #         Provider: CloudFormation
        #       Configuration:
        #         ActionMode: CREATE_UPDATE
        #         RoleArn: !GetAtt CloudFormationDeployRole.Arn
        #         StackName: !Sub ${EnvironmentName}-Server-Pipeline
        #         TemplatePath: Source::deployments/service-pipeline.yml
        #         Capabilities: CAPABILITY_IAM
        #         ParameterOverrides: !Sub |
        #           {
        #             "ServiceName": "server",
        #             "EnvironmentName": "${EnvironmentName}",
        #             "TriggerPattern": "\\[(BuildServer|BuildAll)\\]",
        #             "GitHubRepo": "${GitHubRepo}",
        #             "GitHubBranch": "${GitHubBranch}",
        #             "GitHubUser": "${GitHubUser}"
        #           }
        #       InputArtifacts:
        #         - Name: Source

        #     - Name: Cache
        #       ActionTypeId:
        #         Category: Deploy
        #         Owner: AWS
        #         Version: 1
        #         Provider: CloudFormation
        #       Configuration:
        #         ActionMode: CREATE_UPDATE
        #         RoleArn: !GetAtt CloudFormationDeployRole.Arn
        #         StackName: !Sub ${EnvironmentName}-Cache-Pipeline
        #         TemplatePath: Source::deployments/service-pipeline.yml
        #         Capabilities: CAPABILITY_IAM
        #         ParameterOverrides: !Sub |
        #           {
        #             "ServiceName": "cache",
        #             "EnvironmentName": "${EnvironmentName}",
        #             "TriggerPattern": "\\[(BuildCache|BuildAll)\\]",
        #             "GitHubRepo": "${GitHubRepo}",
        #             "GitHubBranch": "${GitHubBranch}",
        #             "GitHubUser": "${GitHubUser}"
        #           }
        #       InputArtifacts:
        #         - Name: Source

        #     - Name: Database
        #       ActionTypeId:
        #         Category: Deploy
        #         Owner: AWS
        #         Version: 1
        #         Provider: CloudFormation
        #       Configuration:
        #         ActionMode: CREATE_UPDATE
        #         RoleArn: !GetAtt CloudFormationDeployRole.Arn
        #         StackName: !Sub ${EnvironmentName}-Database-Pipeline
        #         TemplatePath: Source::deployments/service-pipeline.yml
        #         Capabilities: CAPABILITY_IAM
        #         ParameterOverrides: !Sub |
        #           {
        #             "ServiceName": "database",
        #             "EnvironmentName": "${EnvironmentName}",
        #             "TriggerPattern": "\\[(BuildDatabase|BuildAll)\\]",
        #             "GitHubRepo": "${GitHubRepo}",
        #             "GitHubBranch": "${GitHubBranch}",
        #             "GitHubUser": "${GitHubUser}"
        #           }
        #       InputArtifacts:
        #         - Name: Source

  #-----------------------------------------------------------------------------#
  # Roles for CodePipeline service
  #-----------------------------------------------------------------------------#
  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
                  - s3:PutObjectAcl
              # 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
              - 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: "*"
              # Allow codepipeline to deploy to ECS
              - Effect: Allow
                Action:
                  - ecs:DescribeServices
                  - ecs:DescribeTaskDefinition
                  - ecs:DescribeTasks
                  - ecs:ListTasks
                  - ecs:RegisterTaskDefinition
                  - ecs:UpdateService
                Resource: "*"

  # This role is passed by CodePipeline to CloudFormation to use
  # when setting up resources in the pipeline
  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:*
                  - rds:*
                  - mq:*
                  # - secretsmanager:*
                  - ssm:*
                  - codebuild:*
                  - ecr:*
                  - codepipeline:*
                  - events:*
                Resource: "*"

  # #-----------------------------------------------------------------------------#
  # # SSM Parameter Store
  # #-----------------------------------------------------------------------------#
  # SSMArtifactBucket:
  #   Type: AWS::SSM::Parameter
  #   Properties:
  #     Name: /Microservices/ArtifactBucket
  #     Type: String
  #     Value: !Ref ArtifactBucket
  # SSMCodePipelineServiceRoleArn:
  #   Type: AWS::SSM::Parameter
  #   Properties:
  #     Name: /Microservices/CodePipelineServiceRoleArn
  #     Type: String
  #     Value: !GetAtt CodePipelineServiceRole.Arn

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