CodePipelineとGiuhubを連携させてECS自動デプロイするCloudformation

# はじめに
CodePipelineをなんとなく使っていたのですが、いろいろな間違いに気づいたので正しい理解をメモしておきます。

# AWSとGithubを連携させるやりかた
特定のブランチ（例えばdevelop）にマージすると自動的にECSサービスが更新されるようにします。
version1とversion2の2つのやり方があり、現在はVersion2が推奨されています。
## 【version1】 GithubのPersonal Access Tokenを使う
ググるとこの方法がよく出てきますが、[今は非推奨とのこと](https://docs.aws.amazon.com/ja_jp/codepipeline/latest/userguide/update-github-action-connections.html)です。
後述するCodeStarSourceConnectionを使いましょう。

> Not recommended: The GitHub version 1 action uses OAuth tokens to authenticate with GitHub and uses a separate webhook to detect changes. This is no longer the recommended method.


```CodePipeline.yml
AWSTemplateFormatVersion: '2010-09-09'

Parameters:
  GitHubRepositoryName:
    Type: String
    Default: <inut-your-github-repo>
  GitHubAccountName:
    Type: String
    Default: <inut-your-github-account>
  GitHubPipelineWebhookName:
    Type: String
    Default: <inut-your-unique-webhook-name> # レポジトリ内の他のwebhookと被らないようにする
  CodeBuildVpcId:
    Type: String
    Default: <input-your-vpc-id-for-codebuild>
  CodeBuildPrivateSubnet1:
    Type: String
    Default: <input-your-subnet-id-for-codebuild>
  CodeBuildPrivateSubnet2:
    Type: String
    Default: <input-your-subnet-id-for-codebuild>
  CodeBuildPrivateSubnet3:
    Type: String
    Default: <input-your-subnet-id-for-codebuild>
  CodeBuildVpcDefaultSG:
    Type: String
    Default: <input-your-default-security-group-for-codebuild>
  EcrRepository:
    Type: String
    Default: <input-your-ECR-repo>
  Branch:
    Type: String
    Default: <input-your-Github-branch-for-CodePipeline>
  ClusterName:
    Type: String
    Default: <inout-your-ECS-cluster-name>
  ServiceName:
    Type: String
    Default: <input-your-ECS-service-name>
  ArtifactFileName:
    Type: String
    Default: artifact.json
  CodeBuildName:
    Type: String
    Default: <input-your-codebuild-name> #なんでもOK
  CodeBuildServiceRoleArn:
    Type: String
    Default: <input-your-service-role-arn>
  CodePipelineRoleArn:
    Type: String
    Default: <input-your-service-role-arn>
  S3ArficactStoreBucketName:
    Type: String
    Default: <input-your-s3-bucket-name>

Resources:
  CodeBuild:
    Type: AWS::CodeBuild::Project
    Properties:
      Name: !Ref CodeBuildName
      ServiceRole: !Ref CodeBuildServiceRoleArn
      Environment:
        ComputeType: BUILD_GENERAL1_SMALL
        Type: LINUX_CONTAINER
        Image: aws/codebuild/standard:5.0
        PrivilegedMode: true
        EnvironmentVariables:
          - Name: AWS_DEFAULT_REGION
            Value: !Sub ${AWS::Region}
          - Name: AWS_ACCOUNT_ID
            Value: !Sub ${AWS::AccountId}
          - Name: REPOSITORY_URI
            Value: !Sub
              - ${AWS::AccountId}.dkr.ecr.${AWS::Region}.amazonaws.com/${Repository}
              - Repository: !Ref EcrRepository
          - Name: CONTAINER_NAME
            Value: !Ref ServiceName
      Artifacts:
        Type: CODEPIPELINE
      Source:
        Type: CODEPIPELINE
        BuildSpec: buildspec.yml
      VpcConfig:
        VpcId: !Ref CodeBuildVpcId
        Subnets:
          - !Ref CodeBuildPrivateSubnet1
          - !Ref CodeBuildPrivateSubnet2
          - !Ref CodeBuildPrivateSubnet3
        SecurityGroupIds:
          - !Ref CodeBuildVpcDefaultSG
        # 今回はCodeBuildを実行するための特定のVPC/Subnet/SGを設定する。これを設定しないと他のAWSユーザと共通IPからdockerイメージをpullすることとなり、pullリミットに引っかかる。
        # Private Subnetの必要があり、NatGWも必要。
        # 詳細 => https://docs.aws.amazon.com/codebuild/latest/userguide/vpc-support.html

  PipelineWebhook:
    Type: 'AWS::CodePipeline::Webhook'
    Properties:
      Authentication: GITHUB_HMAC
      AuthenticationConfiguration:
        SecretToken: '{{resolve:secretsmanager:<your-secret-name>:SecretString:<your-secret-key>}}'
        # Parametersを使って機密情報を入れるのは推奨されない。今回はSecretManagerを使う。
        # 詳細 => https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/dynamic-references.html#dynamic-references-secretsmanager
      Filters:
        - JsonPath: '$.ref'
          MatchEquals: !Sub
            - refs/heads/${Branch}
            - Branch: !Ref Branch
        - JsonPath: '$.created'
          MatchEquals: false
        - JsonPath: '$.deleted'
          MatchEquals: false
        # 特定のBranchの変更(push)を検知してwebhookを実行。このときBranchの作成と消去は無視する。
        # 詳しくは、Githubの Settings => Webhooks => Recent Deliveries => Payload を見ればわかる。
      TargetPipeline: !Ref Pipeline
      TargetAction: SourceAction
      Name: !Ref GitHubPipelineWebhookName
      TargetPipelineVersion: !GetAtt Pipeline.Version
      RegisterWithThirdParty: 'true'

  Pipeline:
    Type: AWS::CodePipeline::Pipeline
    Properties:
      RoleArn: !Ref CodePipelineRoleArn
      Name: !Ref ServiceName
      ArtifactStore:
        Type: S3
        Location: !Ref S3ArficactStoreBucketName
        # 事前にS3バケットを作っておく
      Stages:
        - Name: Source
          Actions:
            - Name: SourceAction
              ActionTypeId:
                Category: Source
                Owner: ThirdParty
                Version: 1
                Provider: GitHub
              Configuration:
                Owner: !Ref GitHubAccountName
                Repo: !Ref GitHubRepositoryName
                PollForSourceChanges: false
                # Webhhokを登録してGithub→AWSに変更を教えてもらうのではなく、AWS側がポーリングすることでGithubの変更をキャッチするための設定。昔はこれが主流だったらしい。今回は使わない。
                # 詳細 => https://aws.amazon.com/jp/premiumsupport/knowledge-center/codepipeline-running-twice/
                Branch: !Ref Branch
                OAuthToken: '{{resolve:secretsmanager:<your-secret-name>:SecretString:<your-secret-key>}}'
              RunOrder: 1
              OutputArtifacts:
                - Name: SourceCode
        - Name: Build
          Actions:
            - Name: Build
              ActionTypeId:
                Category: Build
                Owner: AWS
                Version: 1
                Provider: CodeBuild
              Configuration:
                ProjectName: !Ref CodeBuild
              RunOrder: 1
              InputArtifacts:
                - Name: SourceCode
              OutputArtifacts:
                - Name: BuildOutput
        - Name: Deploy
          Actions:
            - Name: Deploy
              ActionTypeId:
                Category: Deploy
                Owner: AWS
                Version: 1
                Provider: ECS
              Configuration:
                ClusterName: !Ref ClusterName
                ServiceName: !Ref ServiceName
                FileName: !Ref ArtifactFileName
              InputArtifacts:
                - Name: BuildOutput
              RunOrder: 1
```

```buildspec.yml
version: 0.2

phases:
  pre_build:
    commands:
      - IMAGE_URI="${REPOSITORY_URI}:$(echo ${CODEBUILD_RESOLVED_SOURCE_VERSION} | head -c 7)"
      - 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
  build:
    commands:
      - echo Build started on $(date)
      - docker build --tag ${IMAGE_URI} .
      - docker push ${IMAGE_URI}
  post_build:
    commands:
      - echo Build completed on $(date)
      - printf '[{"name":"%s","imageUri":"%s"}]' "${CONTAINER_NAME}" "$IMAGE_URI" > artifact.json

artifacts:
  files: artifact.json
```
```..Dockerfile
# 適当につくってください。
```

## 【version2】 CodeStarSourceConnectionを使う
今はこちらが推奨されているみたいです。
CodeStarSourceConnectionはコンソール( Developer Tools => Settings => Connnections )から作るのが早いです。
https://docs.aws.amazon.com/dtconsole/latest/userguide/connections-create-github.html#connections-create-github-console

下図から、接続先のGithubアカウントまたはレポジトリを選びます。
![スクリーンショット 2022-04-12 11.31.41.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/572098/7ed99903-5e2a-5c8b-8b90-f913fd89b007.png)


下記のようにymlを書き直します。Webhookの設定も不要となります。
> Recommended: The GitHub version 2 action uses Github app-based auth backed by a CodeStarSourceConnection for Bitbucket, GitHub, and GitHub Enterprise Server actions resource. It installs an AWS CodeStar Connections application into your GitHub organization so that you can manage access in GitHub.

```CodePipeline.yml
AWSTemplateFormatVersion: '2010-09-09'

Parameters:
  GitHubRepositoryName:
    Type: String
    Default: <inut-your-github-repo>
  GitHubAccountName:
    Type: String
    Default: <inut-your-github-account>
  CodeBuildVpcId:
    Type: String
    Default: <input-your-vpc-id-for-codebuild>
  CodeBuildPrivateSubnet1:
    Type: String
    Default: <input-your-subnet-id-for-codebuild>
  CodeBuildPrivateSubnet2:
    Type: String
    Default: <input-your-subnet-id-for-codebuild>
  CodeBuildPrivateSubnet3:
    Type: String
    Default: <input-your-subnet-id-for-codebuild>
  CodeBuildVpcDefaultSG:
    Type: String
    Default: <input-your-default-security-group-for-codebuild>
  EcrRepository:
    Type: String
    Default: <input-your-ECR-repo>
  Branch:
    Type: String
    Default: <input-your-Github-branch-for-CodePipeline>
  ClusterName:
    Type: String
    Default: <inout-your-ECS-cluster-name>
  ServiceName:
    Type: String
    Default: <input-your-ECS-service-name>
  ArtifactFileName:
    Type: String
    Default: artifact.json
  CodeBuildName:
    Type: String
    Default: <input-your-codebuild-name> 
  CodeBuildServiceRoleArn:
    Type: String
    Default: <input-your-service-role-arn>
  CodePipelineRoleArn:
    Type: String
    Default: <input-your-service-role-arn>
  S3ArficactStoreBucketName:
    Type: String
    Default: <input-your-s3-bucket-name>
  GithubConnectionArn: # ここに作ったCodeStarSourceConnectionのARNを記入
    Type: String
    Default: <input-your-connection-arn>

Resources:
  CodeBuild:
    Type: AWS::CodeBuild::Project
    Properties:
      Name: !Ref CodeBuildName
      ServiceRole: !Ref CodeBuildServiceRoleArn
      Environment:
        ComputeType: BUILD_GENERAL1_SMALL
        Type: LINUX_CONTAINER
        Image: aws/codebuild/standard:5.0
        PrivilegedMode: true
        EnvironmentVariables:
          - Name: AWS_DEFAULT_REGION
            Value: !Sub ${AWS::Region}
          - Name: AWS_ACCOUNT_ID
            Value: !Sub ${AWS::AccountId}
          - Name: REPOSITORY_URI
            Value: !Sub
              - ${AWS::AccountId}.dkr.ecr.${AWS::Region}.amazonaws.com/${Repository}
              - Repository: !Ref EcrRepository
          - Name: CONTAINER_NAME
            Value: !Ref ServiceName
      Artifacts:
        Type: CODEPIPELINE
      Source:
        Type: CODEPIPELINE
        BuildSpec: buildspec.yml
      VpcConfig:
        VpcId: !Ref CodeBuildVpcId
        Subnets:
          - !Ref CodeBuildPrivateSubnet1
          - !Ref CodeBuildPrivateSubnet2
          - !Ref CodeBuildPrivateSubnet3
        SecurityGroupIds:
          - !Ref CodeBuildVpcDefaultSG

  Pipeline:
    Type: AWS::CodePipeline::Pipeline
    Properties:
      RoleArn: !Ref CodePipelineRoleArn
# 上記RoleにConnectionへのアクセス権限を追加する必要あり。
#{
#    "Version": "2012-10-17",
#    "Statement": [
#      {
#        "Sid": "ConnectionsAccess",
#        "Effect": "Allow",
#        "Action": [
#            "codestar-connections:UseConnection",
#            "codestar-connections:GetConnection",
#            "codestar-connections:ListConnections"
#        ],
#        "Resource": "<input-your-connection-arn>"
#     }
#   ]
#}
      Name: !Ref ServiceName
      ArtifactStore:
        Type: S3
        Location: !Ref S3ArficactStoreBucketName
      Stages:
        - Name: Source # updated
          Actions:
            - Name: SourceAction
              ActionTypeId:
                Category: Source
                Owner: AWS
                Version: 1
                Provider: CodeStarSourceConnection
              OutputArtifacts:
                - Name: SourceCode
              Configuration:
                FullRepositoryId: !Sub
                  - ${OrganizationName}/${RepositoryName}
                  - OrganizationName: !Ref GitHubAccountName
                    RepositoryName: !Ref GitHubRepositoryName
                ConnectionArn: !Ref GithubConnectionArn
                BranchName: !Ref Branch
                OutputArtifactFormat: CODEBUILD_CLONE_REF
              RunOrder: 1
        - Name: Build
          Actions:
            - Name: Build
              ActionTypeId:
                Category: Build
                Owner: AWS
                Version: 1
                Provider: CodeBuild
              Configuration:
                ProjectName: !Ref CodeBuild
              RunOrder: 1
              InputArtifacts:
                - Name: SourceCode
              OutputArtifacts:
                - Name: BuildOutput
        - Name: Deploy
          Actions:
            - Name: Deploy
              ActionTypeId:
                Category: Deploy
                Owner: AWS
                Version: 1
                Provider: ECS
              Configuration:
                ClusterName: !Ref ClusterName
                ServiceName: !Ref ServiceName
                FileName: !Ref ArtifactFileName
              InputArtifacts:
                - Name: BuildOutput
              RunOrder: 1
```


↓こちらの記事を参考にさせていただきました。

https://qiita.com/furu8ma/items/a35bfa4dc6504f8d3422

https://docs.aws.amazon.com/ja_jp/codebuild/latest/userguide/sample-docker.html