CFnによるECS on FargateのBlue/Green DeploymentをCodePiplineで実装した手順

# 目次
1. [Networkの構築](#network)
2. [ECS on Fargateの構築](#ecs)
3. [Pipelineの構築](#pipeline)


# <a id='network'>1. Networkの構築<a>
ここでは、VPC, Subnet, RouteTable, InternetGateway, VPC Endpoint, Security Groupを作成する

詳細は以下を参考

# <a id='ecs'>2. ECS on Fargateの構築</a>
1. [クラスターとロググループ作成](#cluster)
2. [タスク実行ロール作成](#taskExecutionRole)
3. [タスク定義作成](#taskDefinitions)
4. [ロードバランサー作成](#loadbalancer)
5. [VPC Endpoint作成](#vpce)

## <a id="cluster">クラスター作成</a>

```yaml
  ECSClusterBackendApplication:
    Type: AWS::ECS::Cluster
    Properties:
      ClusterName: !Sub "${ProjectName}-backend-application"
```

コンテナ内では基本的に永続データを保持しないような設計にするために、ログデータを外に吐き出す必要がある
`Fluentd`などがあるがここでは、`CloudWatch Logs`を使用する
Log groupの作成をしておき、ここで作成したロググループをTask定義で使用することとする

```yaml
  CloudWatchLogGroupBackendApplication:
    Type: AWS::Logs::LogGroup
    Properties:
      LogGroupName: !Sub "/ecs/logs/${ProjectName}-backend-application"
      RetentionInDays: 7
```

## <a id="taskExecutionRole">タスク実行ロールの作成</a>

タスク実行ロールとは、ECSコンテナエージェントから他のAWSサービスへアクセスするためのロールを定義するもの
似ているロールとしてタスクロールが存在するが、これはコンテナ内から他のAWSサービスへアクセスするためのロールを定義するものとなる



```yaml
  ECSTaskExecutionRole:
    Type: AWS::IAM::Role
    Properties:
      RoleName: !Sub "${ProjectName}-ECSTaskExecutionRolePolicy"
      AssumeRolePolicyDocument:
        Version: 2012-10-17
        Statement:
          - Effect: Allow
            Principal:
              Service: ecs-tasks.amazonaws.com
            Action: sts:AssumeRole
      ManagedPolicyArns:
        - arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy
```

## <a id="taskDefinitions">タスク定義の作成</a>

[TaskDefinitions](https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/aws-resource-ecs-taskdefinition.html)

タスク定義とはコンテナ群やタスクのためのCPU、Memory、ネットワークの設定などを記述する

CPUやMemoryを記入する部分が二箇所あるがこれはタスク自体のためのCPUやMemoryとコンテナのためのものとの２つがある

CPUによって利用可能なMemoryの値の範囲が決まる
逆にMemoryによってCPUの利用可能な値の範囲が決まる

Fargateを使用している場合タスクレベルのCPU、Memoryの指定は必須である

Fargateを使用している場合、コンテナ定義する際のCPUやメモリーの指定はオプションであるが、タスクレベルのCPUやメモリーで指定されている値を超えてはいけない

Essentialをtrueにしておくことで、そのコンテナが何かしらの理由でダウンした場合そのタスク内の他のコンテナも停止させる

`MemoryReservation`
コンテナ用に予約されるメモリ量であり、システムメモリの競合が激しい場合このメモリ値までは使用するよう試みる。しかし、コンテナがよりメモリを消費したい場合は、`Memory` parameterで指定された値かコンテナインスタンスの残りのリソース量まで使用することができる

[ContainerDefinitions](https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/aws-properties-ecs-taskdefinition-containerdefinitions.html)


```yaml
  ECSTaskDefinitionBackendApplication:
    Type: AWS::ECS::TaskDefinition
    Properties:
      Family: !Sub "${ProjectName}-backend-application-def"
      Cpu: !Ref ECSBackendApplicationTaskCPUUnit
      Memory: !Ref ECSBackendApplicationTaskMemory
      ExecutionRoleArn: !Ref ECSTaskExecutionRole
      NetworkMode: awsvpc
      RequiresCompatibilities:
        - FARGATE
      ContainerDefinitions:
        - Name: !Sub "${ProjectName}-backend-application"
          Image: !Ref ECSBakendApplicationImageName
          Essential: true
          Cpu: 256
          MemoryReservation: 128
          PortMappings:
            - ContainerPort: 80
              HostPort: 80
              Protocol: tcp
          LogConfiguration:
            LogDriver: awslogs
            Options:
              awslogs-group: !Ref CloudWatchLogGroupBackendApplication
              awslogs-region: !Ref AWS::Region
              awslogs-stream-prefix: ecs
```

## <a id="loadbalancer">ロードバランサー作成</a>
まずはロードバランサーのみの作成をしていく

`Scheme: internet-facing`を指定することで、インターネットの世界とのやりとりができるようになる
`internal`を指定するとプライベートIP addressを扱いVPC内でのやり取りしかできない

`LoadBalancerAttributes`については[こちら](https://choco.kibe.la/notes/7#loadbalancerattributes)


```yaml
  InternetALB:
    Type: AWS::ElasticLoadBalancingV2::LoadBalancer
    Properties:
      Name: !Sub "${ProjectName}-ingress-alb"
      Scheme: internet-facing
      LoadBalancerAttributes:
        - Key: deletion_protection.enabled
          Value: false
        - Key: idle_timeout.timeout_seconds
          Value: 120
        - Key: access_logs.s3.enabled
          Value: true
        - Key: access_logs.s3.bucket
          Value: !Sub "${ProjectName}-alb-log-${AWS::AccountId}"
      SecurityGroups:
        - !Ref ALBSecurityGroupId
      Subnets:
        - !Ref ALBSubnetId1
        - !Ref ALBSubnetId2
      Type: application
      Tags:
        - Key: Name
          Value: !Sub "${ProjectName}-internet-application-loadbalancer"
```

次にロードバランサーがルーティングする対象となるターゲットグループを作成する

`AWS::ECS::TaskDefinitons`パラメータの中の`ContainerDefinitions`でヘルスチェックを指定していなかったのでここで設定している

今回は、Blue/Green Deploymentを想定しているので、ここでは記載しないがGreen用のターゲットグループも作成しておくこと

```yaml
  TargetGroupBackendApplicationBlue:
    Type: AWS::ElasticLoadBalancingV2::TargetGroup
    Properties:
      VpcId: !Ref VpcId
      Name: !Sub "${ProjectName}-tg-backend-application-blue"
      Protocol: HTTP
      Port: 80
      TargetType: ip
      HealthCheckIntervalSeconds: 15
      HealthCheckPath: /healthcheck
      HealthCheckTimeoutSeconds: 5
      HealthyThresholdCount: 3
      UnhealthyThresholdCount: 3
      Matcher:
        HttpCode: 200
```

最後に、ロードバランサーとターゲットグループの紐付けを行う

```yaml
  ALBIngressListner1:
    Type: AWS::ElasticLoadBalancingV2::Listener
    Properties:
      DefaultActions:
        - Type: forward
          TargetGroupArn: !Ref TargetGroupBackendApplicationBlue
      LoadBalancerArn: !Ref InternetALB
      Protocol: HTTP
      Port: 80
```

## <a id='vpce'>VPC Endpoint作成</a>
ECSによりあるサブネットにFargate(Container)を起動させるためには、ECRからimageをpullしたりしなければならない
これらのAWSサービスはVPC内に存在しているわけではないので本来ならばNAT Gatewayなどを通ってインターネットの世界に出た後に望むAWSサービスへとアクセスする必要がある
しかし、VPC Endpointを使用することでNAT Gatewayを用意することなく他サービスと連携することができる

ECRと接続するためには、`ecr.qpi` `ecr.dkr`の両方が必要になってくる
コンテナ内からログを出力するためとして`logs`
これらはVPC Endpointの`interface`を使用している

S3は今後CodePipelineなどでartifactを出力するために作成してある
また、S3のみ`Gateway`となっている

`gateway`型は、`Internet gateway`などと同じようにこのgatewayにルーティングされるよう対象のサブネットに関連づけられているルートテーブルに追加する必要がある
この設定は以下のように`RouteTableIds`で設定する

```yaml
 EcrApiEndpoint:
    Type: AWS::EC2::VPCEndpoint
    Properties:
      ServiceName: !Sub "com.amazonaws.${AWS::Region}.ecr.api"
      PrivateDnsEnabled: true
      SecurityGroupIds:
        - !Ref EndpointSecurityGroupId
      SubnetIds:
        - !Ref VPCEndpointSubnetId1
        - !Ref VPCEndpointSubnetId2
      VpcEndpointType: Interface
      VpcId: !Ref VpcId

  EcrDkrEndpoint:
    Type: AWS::EC2::VPCEndpoint
    Properties:
      ServiceName: !Sub "com.amazonaws.${AWS::Region}.ecr.dkr"
      PrivateDnsEnabled: true
      SecurityGroupIds:
        - !Ref EndpointSecurityGroupId
      SubnetIds:
        - !Ref VPCEndpointSubnetId1
        - !Ref VPCEndpointSubnetId2
      VpcEndpointType: Interface
      VpcId: !Ref VpcId

  S3Endpoint:
    Type: AWS::EC2::VPCEndpoint
    Properties:
      ServiceName: !Sub "com.amazonaws.${AWS::Region}.s3"
      RouteTableIds:
        - !Ref VPCGatewayRouteTableId
      VpcEndpointType: Gateway
      VpcId: !Ref VpcId

  LogsEndpoint:
    Type: AWS::EC2::VPCEndpoint
    Properties:
      ServiceName: !Sub "com.amazonaws.${AWS::Region}.logs"
      PrivateDnsEnabled: true
      SecurityGroupIds:
        - !Ref EndpointSecurityGroupId
      SubnetIds:
        - !Ref VPCEndpointSubnetId1
        - !Ref VPCEndpointSubnetId2
      VpcEndpointType: Interface
      VpcId: !Ref VpcId
```

# <a id='pipelien'>3. CodePipelineの構築</a>

1. [IAM Roleの作成](#iamRole)
2. [事前準備（リソース）](#resource)
3. [CodeBuildの作成](#codebuild)
4. [CodePipelineの作成](#codepipeline)

## <a id="iamRole">IAM Roleの作成</a>

`CodePipline`を使用してデプロイする場合、`CodeBuild`で`ECR`からdocker imageをpullするための権限などを作成していく

### CodeWatchEvent
`CloudWatchEvent`から`CodePipeline`に対してパイプラインを開始させるためのポリシーを設定している

```yaml
  Type: AWS::IAM::Role
    Properties:
      RoleName: !Sub ${ProjectName}-CloudWatchEventRole
      AssumeRolePolicyDocument:
        Version: 2012-10-17
        Statement:
          - Effect: Allow
            Principal:
              Service:
                - events.amazonaws.com
            Action: sts:AssumeRole
      Path: /
      Policies:
        - PolicyName: CloudWatchEventsPipelineExecution
          PolicyDocument:
            Version: 2012-10-17
            Statement:
              - Effect: Allow
                Action: codepipeline:StartPipelineExecution
                Resource: !Sub arn:aws:codepipeline:${AWS::Region}:${AWS::AccountId}:${Pipeline}
```

### CodeBuild
- ログの作成変更
- ArtifactのためのS3操作
-  `codebuild`関連は未調査
- `ECR`からimageをpullするあたりの操作

など

```yaml
  CodeBuildServiceRole:
    Type: AWS::IAM::Role
      Properties:
        RoleName: !Sub ${ProjectName}-CodeBuildRole
        Path: /
        AssumeRolePolicyDocument:
          Version: '2012-10-17'
          Statement:
            - Effect: Allow
              Principal:
                Service: codebuild.amazonaws.com
              Action: sts:AssumeRole
        Policies:
          - PolicyName: !Sub ${ProjectName}CodeBuildAccess
            PolicyDocument:
              Version: 2012-10-17
              Statement:
                - Effect: Allow
                  Resource: '*'
                  Action:
                    - logs:CreateLogGroup
                    - logs:CreateLogStream
                    - logs:PutLogEvents
                - Effect: Allow
                  Resource: !Sub arn:aws:s3:::${ArtifactBucket}/*
                  Action:
                    - s3:PutObject
                    - s3:GetObject
                    - s3:GetObjectVersion
                    - s3:GetBucketAcl
                    - s3:GetBucketLocation
                - Effect: Allow
                  Action:
                    - codebuild:CreateReportGroup
                    - codebuild:CreateReport
                    - codebuild:UpdateReport
                    - codebuild:BatchPutTestCases
                    - codebuild:BatchPutCodeCoverages
                  Resource: '*'
                - Effect: Allow
                  Action:
                    - ecr:GetAuthorizationToken
                    - ecr:BatchCheckLayerAvailability
                    - ecr:GetDownloadUrlForLayer
                    - ecr:GetRepositoryPolicy
                    - ecr:DescribeRepositories
                    - ecr:ListImages
                    - ecr:DescribeImages
                    - ecr:BatchGetImage
                    - ecr:InitiateLayerUpload
                    - ecr:UploadLayerPart
                    - ecr:CompleteLayerUpload
                    - ecr:PutImage
                  Resource: '*'
```

### CodePipeline

`ecs-tasks`に対してロールを渡す権限を付与している
これは、Blue/Green デプロイタイプを使用する Amazon ECS サービス内のタスクでタスク実行ロールまたはタスクロール上書きを使用する必要がある場合は、各タスクのタスク実行ロールまたはタスクロール上書きに対する iam:PassRole アクセス許可を、CodeDeploy IAM ロールにインラインポリシーとして追加する必要があるから
具体例は未調査である

[iam:PassRoleの付与する理由（最後らへんに記述されてる）](https://docs.aws.amazon.com/ja_jp/AmazonECS/latest/developerguide/codedeploy_IAM_role.html)

`iam:PassRole`については[こちら](https://choco.kibe.la/notes/21)

AWSのコンソール側でCodePipelineを作成すると「新しいサービスロールを作成」を選択でき、これにより自動で作成されたロールがCodePipelineにアタッチされる
このロールが持つポリシーを参考にするのが良いと思う

```yaml
  CodePipelineServiceRole:
    Type: AWS::IAM::Role
    Properties:
      RoleName: !Sub ${ProjectName}-CodePipelineRole
      Path: /
      AssumeRolePolicyDocument:
        Version: 2012-10-17
        Statement:
          - Effect: Allow
            Principal:
              Service: codepipeline.amazonaws.com
            Action: sts:AssumeRole
      Policies:
        - PolicyName: !Sub ${ProjectName}PipelinePolicy
          PolicyDocument:
            Version: 2012-10-17
            Statement:
              - Action:
                  - iam:PassRole
                Resource: '*'
                Effect: Allow
                Condition:
                  StringEqualsIfExists:
                    iam:PassedToService:
                      - ecs-tasks.amazonaws.com
              - Resource:
                  - !Sub arn:aws:s3:::${ArtifactBucket}/*
                Effect: Allow
                Action:
                  - s3:PutObject
                  - s3:GetObject
                  - s3:GetObjectVersion
                  - s3:GetBucketVersioning
              - Action:
                  - codecommit:CancelUploadArchive
                  - codecommit:GetBranch
                  - codecommit:GetCommit
                  - codecommit:GetRepository
                  - codecommit:GetUploadArchiveStatus
                  - codecommit:UploadArchive
                Resource: '*'
                Effect: Allow
              - Action:
                  - codedeploy:CreateDeployment
                  - codedeploy:GetApplication
                  - codedeploy:GetApplicationRevision
                  - codedeploy:GetDeployment
                  - codedeploy:GetDeploymentConfig
                  - codedeploy:RegisterApplicationRevision
                  - codedeploy:*
                Resource: '*'
                Effect: Allow
              - Action:
                  - elasticbeanstalk:*
                  - ec2:*
                  - elasticloadbalancing:*
                  - autoscaling:*
                  - cloudwatch:*
                  - sns:*
                  - cloudformation:*
                  - rds:*
                  - sqs:*
                  - ecs:*
                Resource: '*'
                Effect: Allow
              - Action:
                  - codebuild:BatchGetBuilds
                  - codebuild:StartBuild
                  - codebuild:BatchGetBuildBatches
                  - codebuild:StartBuildBatch
                Resource: '*'
                Effect: Allow
```

## <a id="resource">事前準備（リソース）</a>
ここでは、CodePipelineを構築する前に必要となるリソースを手動やCFnで作成する


### ECR、ECS

それぞれ、手動でコンソールから作成してください！

### S3
 CodeBuildで作成されたArtifacts(成果物)などを保存しておくためのS3を作成

```yaml
  ArtifactBucket:
    Type: AWS::S3::Bucket
    Properties:
      PublicAccessBlockConfiguration:
        BlockPublicAcls: True
        BlockPublicPolicy: True
        IgnorePublicAcls: True
        RestrictPublicBuckets: True
```

### CloudWatch Event
CodeCommitのリポジトリで`main`ブランチの状態変更を検出するCloudWatch Eventを作成

`referenceCreated`: 対象のブランチが作成されたとき
`referenceUpdated`:  対象のブランチがマージなどによって更新されたとき
[CodeCommit イベントのモニタリング](https://docs.aws.amazon.com/ja_jp/codecommit/latest/userguide/monitoring-events.html)
[CloudWatch Events Event Examples From Supported Services](https://docs.aws.amazon.com/AmazonCloudWatch/latest/events/EventTypes.html)
[CloudWatch Events のイベントパターン](https://docs.aws.amazon.com/ja_jp/AmazonCloudWatch/latest/events/CloudWatchEventsandEventPatterns.html)

上記のイベント発生時に`codepipeline`を起動させる

```yaml
  AmazonCloudWatchEventRule:
    Type: AWS::Events::Rule
    Properties:
      EventPattern:
        source:
          - aws.codecommit
        detail-type:
          - CodeCommit Repository State Change
        resources:
          - Fn::Join:
              - ''
              - - 'arn:aws:codecommit:'
                - !Ref 'AWS::Region'
                - ':'
                - !Ref 'AWS::AccountId'
                - ':'
                - !Ref CodeCommitRepositoryName
        detail:
          event:
            - referenceCreated
            - referenceUpdated
          referenceType:
            - branch
          referenceName:
            - main
      Targets:
        - Arn: !Sub arn:aws:codepipeline:${AWS::Region}:${AWS::AccountId}:${Pipeline}
          RoleArn: !GetAtt CloudwatchEventRole.Arn
          Id: codepipeline-AppPipeline
```

### Deployment Groupの作成
CodePipelineのパラメータに指定する必要があるDeploy用のApplicationsとDeploymentGroupの作成を手動で行う

なぜCFnで作成しないかというと現状、CodeDeployによる ECS blue/green deploymentsのDeployment Group作成にAWSが対応していない

https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/aws-resource-codedeploy-deploymentgroup.html

> Amazon ECS blue/green deployments through CodeDeploy do not use the AWS::CodeDeploy::DeploymentGroup resource. To perform Amazon ECS blue/green deployments, use the AWS::CodeDeploy::BlueGreen hook.

CFnで完結させる方法もあるが、いくつか制約があるので今回は手動作成で行う
デプロイ用のアプリケーションはApplication NameとCompute typeを指定するのみ

Deployment Groupに関しては、
`Deployment group name`, `Service role`, ` ECS cluster name`, `Load balancers`, `Target group`などの指定が必要になる
クラスターやロードバランサーなどは
**2. ECS on Fargateの構築**
で作成したCFnによって構築されるものを指定する必要がある


## <a id="codebuild">CodeBuildの作成</a>
CodeBuildではDocker imageの作成やテストなどを行うことができる
今回はDocker imageを作成しECRにpushする処理を行う

### `Artifacts`
`CodeBuild`によって作成されるartifactsの出力設定を指定する

- `Type`
artifactの種類 `CODEPIPELINE | NO_ARTIFACTS | S3`

- `Location`
CodeBuildが生成するartifactの出力場所
`Type`によって有効な値が異なる
`CODEPIPELINE` => CodePipelineが出力場所を管理するのでこの値は無視される
`NO_ARTIFACTS` => artifactがないので無視される
`S3`=> bucket name

### `Source`
CodeBuildで処理する対象となるソースコードの設定を指定する

[AWS::CodeBuild::Project](https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/aws-resource-codebuild-project.html#cfn-codebuild-project-servicerole)

- `Type`
ソースコードが存在するリポジトリタイプ
`CODECOMMIT` `CODEPIPELINE` `Github` `S3`などなど
`CODEPIPELINE`が指定された場合ソースコードに関する設定はCodePipelineが管理する

- `Location`
`Type`で指定したリポジトリタイプに応じて実際のソースコードが存在するURLなどを指定する

- `BuildSpec`
CodeBuildで行う実際の処理を記述したもの
この値が設定されなかった場合はプロジェクトのルートパス配下に`.buildspec.yml`ファイルを置く必要がある

- `Environment`
CodeBuildを実行する環境の設定


```yaml
  CodeBuildProject:
   Type: AWS::CodeBuild::Project
   Properties:
    Name: !Ref AWS::StackName
    ServiceRole: !Ref CodeBuildServiceRole
    Artifacts:
     Type: CODEPIPELINE
    Source:
      Type: CODEPIPELINE
      BuildSpec: |
        version: 0.2
          phases:
            pre_build:
              commands:
                - echo Logging in to Amazon ECR...
                - $(aws ecr get-login --no-include-email)
                - IMAGE_TAG=$(echo $CODEBUILD_RESOLVED_SOURCE_VERSION | cut -c 1-7)
            build:
              commands:
                - echo Build started on `date`
                - echo Building the Docker image...
                - docker build -t $REPOSITORY_URI:$IMAGE_TAG .
                - docker tag $REPOSITORY_URI:$IMAGE_TAG $REPOSITORY_URI:$IMAGE_TAG
            post_build:
              commands:
                - echo Build completed on `date`
                - echo Pushing the Docker images...
                - docker push $REPOSITORY_URI:$IMAGE_TAG
                - echo Writing imageDetail json...
                - echo "{\"name\":\"${ContainerName}\",\"ImageURI\":\"${REPOSITORY_URI}:${IMAGE_TAG}\"}" > imageDetail.json
          artifacts:
            files: imageDetail.json
    Environment:
      PrivilegedMode: true
      ComputeType: BUILD_GENERAL1_SMALL
      Image: aws/codebuild/standard:4.0
      Type: LINUX_CONTAINER
      EnvironmentVariables:
          - Name: AWS_DEFAULT_REGION
            Value: !Ref AWS::Region
          - Name: REPOSITORY_URI
            Value: !Sub ${AWS::AccountId}.dkr.ecr.${AWS::Region}.amazonaws.com/${ECRName}
          - Name: ContainerName
            Value: !Ref ContainerName
          - Name: DOCKER_BUILDKIT
            Value: '1'
```

## <a id="pipeline">CodePipelineの作成</a>
Source,  Build, Deployなど一連のCI/CDの流れを管理するPipelineを指定する

`ArtifactStore`では、CodeBuildで指定しなかったArtifactの保存先の設定を行う
ドキュメント見た感じS3のみ指定可能で、S3 bucketの名前を指定する　

`InputArtifacts`、`OutputArtifacts`を指定することによって各ステージでのArtifactに関する入出力の対象となるものを指定する

各ステージで利用可能な`Configuration`などのプロパティの値は以下の記事を参考
[Action structure requirements in CodePipeline](https://docs.aws.amazon.com/codepipeline/latest/userguide/reference-pipeline-structure.html#action-requirements)
[CodeCommit](https://docs.aws.amazon.com/codepipeline/latest/userguide/action-reference-CodeCommit.html)
[CodeBuild](https://docs.aws.amazon.com/codepipeline/latest/userguide/action-reference-CodeBuild.html)
[CodeDeployToECS](https://docs.aws.amazon.com/codepipeline/latest/userguide/action-reference-CodeDeploy.html)

```yaml
  Pipeline:
    Type: AWS::CodePipeline::Pipeline
    Properties:
      RoleArn: !GetAtt CodePipelineServiceRole.Arn
      Name: !Sub ${ProjectName}-pipeline
      ArtifactStore:
        Type: S3
        Location: !Ref ArtifactBucket
      Stages:
        - Name: Source
          Actions:
            - Name: SourceAction
              ActionTypeId:
                Category: Source
                Owner: AWS
                Version: '1'
                Provider: CodeCommit
              Configuration:
                RepositoryName: !Ref CodeCommitRepositoryName
                PollForSourceChanges: false
                BranchName: main
              RunOrder: 1
              OutputArtifacts:
                - Name: SourceArtifact
        - Name: Build
          Actions:
            - Name: Build
              ActionTypeId:
                Category: Build
                Owner: AWS
                Version: '1'
                Provider: CodeBuild
              Configuration:
                ProjectName: !Ref CodeBuildProject
              RunOrder: 1
              InputArtifacts:
                - Name: SourceArtifact
              OutputArtifacts:
                - Name: BuildOutput
        # - Name: Approval
        #   Actions:
        #     - Name: Manual_Approval
        #       ActionTypeId:
        #         Category: Approval
        #         Owner: AWS
        #         Version: '1'
        #         Provider: Manual
        #       Configuration:
        #         CustomData: !Sub '${ServiceName} will be updated. Do you want to deploy it?'
        #         NotificationArn: arn:aws:sns:ap-northeast-1:xxxxxxxx:hogehoge
        - Name: Deploy
          Actions:
            - Name: Deploy
              ActionTypeId:
                Category: Deploy
                Owner: AWS
                Version: '1'
                Provider: CodeDeployToECS
              Configuration:
                AppSpecTemplateArtifact: App
                AppSpecTemplatePath: appspec.yaml
                TaskDefinitionTemplateArtifact: App
                TaskDefinitionTemplatePath: taskdef.json
                ApplicationName: !Ref CodeDeployAppName
                DeploymentGroupName: !Ref CodeDeployDGName
                Image1ArtifactName: BuildOutput
                Image1ContainerName: IMAGE1_NAME
              RunOrder: 1
              InputArtifacts:
                - Name: App
                - Name: BuildOutput
              Region: !Ref AWS::Region
```

## taskdef.json
ECSのタスク定義を記述したファイル
`<IMAGE1_NAME>`はプレースホルダーとして機能する

以下のようなファイルは **2.** で行ったECS構築の際に作成したタスク定義からJSON formatをコピーして`image` propertyの部分を変更するなどして作成すると良い

```json
{
  "executionRoleArn": "arn:aws:iam::<AccountID>:role/<ProjectName>-ecsTaskExecutionRole",
  "containerDefinitions": [
    {
      "name": "<ProjctName>-backend-application",
      "image": "<IMAGE1_NAME>",
      "essential": true,
      "portMappings": [
        {
          "hostPort": 80,
          "protocol": "tcp",
          "containerPort": 80
        }
      ]
    }
  ],
  "requiresCompatibilities": ["FARGATE"],
  "networkMode": "awsvpc",
  "cpu": "256",
  "memory": "512",
  "family": "ecs-demo"
}
```

## appspec.yml
デプロイ手順をまとめたもの
`<TASK_DEFINITION>`はプレースホルダーでありデプロイステージで自動的に対象のタスク定義のバージョンに置き換わる

`ContainerName` : これは、Amazon ECS アプリケーションを含む Amazon ECS コンテナの名前。Amazon ECS タスク定義で指定されたコンテナにする必要があります。

```yaml
version: 1
Resources:
  - TargetService:
      Type: AWS::ECS::Service
      Properties:
        TaskDefinition: <TASK_DEFINITION>
        LoadBalancerInfo:
          ContainerName: "<ProjectName>-backend-application"
          ContainerPort: 80
```

`taskdef.json` `appspec.yaml`ともにプレースホルダーを使用しているがここにはどこから何が入るのだろうが

ここらは予想になってしまう
まず、CodeBuildでartifactとして
```
echo "{\"name\":\"${ContainerName}\",\"ImageURI\":\"${REPOSITORY_URI}:${IMAGE_TAG}\"}" > imageDetail.json
```
上記のファイルを出力している
このファイルに`imageURI`が含まれている。これが、`<IMAGE1_NAME>`のプレースホルダーに代入されるのではないか

次に`appspec.yaml`にて`TASK_DEFINITION`が使用されているがここには`taskdef.json`によって作成されたタスク定義が代入されるのではないかと予想している


# 次にやりたいこと
- Sorce ProviderをCode CommitからGithubに変更
- Code BuildをCircle CIに移行
- CodePiplienを廃止し、CodeDeployのみAWSで実行
- Deploy完了を通知
- Fargateを使用しない間停止させる
- Terraformでで構築