【AWS】 (中級者向け) CodePipelineを使ったECSコンテナのデプロイ自動化 その3(CloudFormationでインフラコード化)

# 概要
CodePipelineでコンテナデプロイを実行するCICD構築をまとめました。
本記事は[【AWS】 (初心者向け) CodePipelineを使ったECSコンテナのデプロイ自動化 その2(CodeCommit, CodeBuild, CodePipeline)](https://qiita.com/ramunauna/items/114ffb3b5532cdb7fd2d)の続きです。
CodePipelineでのECSデプロイ環境をコード化し、CloudFormationで構築します。

# 目次
記事を3つに分割しました。
1. コンテナ構築 / 起動
    1-1. Flaskアプリケーションの作成
    1-2. ローカル環境でコンテナの作成 / 起動確認
    1-3. ECRへイメージをプッシュ
    1-4. ECSでコンテナ起動

1. CICDパイプラインの作成
    2-1. CodeCommitの作成
    2-2. CodeBuildの作成
    2-3. CodePipelineの作成

1. 上記のコンテナ / CICDパイプライン構築をコード化
    3-1. CloudFormationテンプレート __←本記事はここから__

前の記事はこちら
[【AWS】 (初心者向け) CodePipelineを使ったECSコンテナのデプロイ自動化 その1(ECR, ECS Fargate)](https://qiita.com/ramunauna/items/6417925934441b9fdc78)
[【AWS】 (初心者向け) CodePipelineを使ったECSコンテナのデプロイ自動化 その2(CodeCommit, CodeBuild, CodePipeline)](https://qiita.com/ramunauna/items/114ffb3b5532cdb7fd2d)

# 構成図
最終的な構成はこちら
![aws構成図.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1813991/f4547411-56f7-7d9e-eaca-0422e14b7d9b.png)

# 3. ECSコンテナ / CICDパイプライン構築をコード化
パイプライン構築は扱うサービスも設定項目も多いので複数環境作成する際に手間がかかりますし、意図せぬ環境差分を作ってしまう恐れがあります。
そのため関連リソースをコード化してCloudFormationで構築する流れをまとめます。

リソース依存関係等を踏まえた上で下記の構築フローとなりました。
拡張性やファイル管理を考慮してなるべくAWSサービスごとにテンプレートファイルを分割しています。

1. network.yml
1. ecr.yml (イメージのプッシュまで)
1. ecs.yml
1. codecommit.yml (ローカルにクローン)
1. s3.yml
1. codebuild.yml (ビルドの実行確認)
1. codepipeline.yml (パイプラインの実行確認)
1. eventbridge.yml (ソースの更新、developブランチへマージ→パイプラインの起動確認)

__※ymlファイルが長いため折りたたみで表示しています。__

### 3-1. network.yml
ECSクラスター作成時にAWS側で自動作成していたネットワークリソースを作成します。
構築リソースは下記。
- VPC(&セキュリティグループ)
- サブネット
- ルートテーブル
- インターネットゲートウェイ

<details>
<summary>network.yml</summary>
<div>

```yml:network.yml
AWSTemplateFormatVersion: "2010-09-09"
Description: "Cloudformation template for Network"

########################################
# Parameters
########################################
Parameters:
  ProjectName:
    Type: "String"
    Description: "Project Name"
  Environment:
    Type: "String"
    AllowedValues:
      - "dev"
      - "stg"
      - "prd"
    Description: "Environment"
  VpcCidr:
    Type: "String"
    Default: "10.0.0.0/16"
    Description: "VPC CIDR"
  SubnetCidr:
    Type: "String"
    Default: "10.0.0.0/24"
    Description: "Subnet CIDR"
  AvailabilityZone:
    Type: "String"
    Default: "ap-northeast-1a"
    Description: "Availability Zone"

########################################
# Resources
########################################
Resources:
  # VPC
  VPC:
    Type: AWS::EC2::VPC
    Properties:
      CidrBlock: !Ref VpcCidr
      Tags:
        - Key: "Name"
          Value: !Sub "${ProjectName}-${Environment}-vpc"

  # Subnet
  Subnet:
    Type: AWS::EC2::Subnet
    Properties:
      CidrBlock: !Ref SubnetCidr
      AvailabilityZone: !Ref AvailabilityZone
      VpcId: !Ref VPC
      Tags:
        - Key: "Name"
          Value: !Sub "${ProjectName}-${Environment}-subnet"

  # Internet Gateway
  IGW:
    Type: AWS::EC2::InternetGateway
    Properties:
      Tags:
        - Key: "Name"
          Value: !Sub "${ProjectName}-${Environment}-igw"
  ## InternetGatewayのアタッチ
  IGWAttach:
    Type: AWS::EC2::VPCGatewayAttachment
    Properties:
      InternetGatewayId: !Ref IGW
      VpcId: !Ref VPC

  # Route Table
  RouteTable:
    Type: AWS::EC2::RouteTable
    Properties:
      VpcId: !Ref VPC
      Tags:
        - Key: "Name"
          Value: !Sub "${ProjectName}-${Environment}-rtb"
  ## パブリックルート
  PublicRoute:
    Type: AWS::EC2::Route
    Properties:
      DestinationCidrBlock: "0.0.0.0/0"
      GatewayId: !Ref IGW
      RouteTableId: !Ref RouteTable
  ## サブネットへの関連付け
  SubnetRouteTableAssociation:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      RouteTableId: !Ref RouteTable
      SubnetId: !Ref Subnet

  # Security Group
  SecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupName: !Sub "${ProjectName}-${Environment}-sg-http"
      GroupDescription: !Sub "${ProjectName}-${Environment}-sg-http"
      VpcId: !Ref VPC
      SecurityGroupIngress:
        - IpProtocol: "tcp"
          FromPort: 80
          ToPort: 80
          CidrIp: "0.0.0.0/0"
      Tags:
        - Key: "Name"
          Value: !Sub "${ProjectName}-${Environment}-sg-http"

########################################
# Output
########################################
Outputs:
  VpcId:
    Value: !Ref VPC
    Export:
      Name: "VpcId"
  SubnetId:
    Value: !Ref Subnet
    Export:
      Name: "SubnetId"
  SecurityGroupId:
    Value: !Ref SecurityGroup
    Export:
      Name: "SecurityGroupId"
```

</div>
</details>

### 3-2. ecr.yml
ECRリポジトリを作成します。
<details>
<summary>ecr.yml</summary>

```yml:ecr.yml
AWSTemplateFormatVersion: "2010-09-09"
Description: "Cloudformation template for ECR"

########################################
# Parameters
########################################
Parameters:
  ProjectName:
    Type: "String"
    Description: "Project Name"
  Environment:
    Type: "String"
    AllowedValues:
      - "dev"
      - "stg"
      - "prd"
    Description: "Environment"

########################################
# Resources
########################################
Resources:
  # ECR
  ECR:
    Type: AWS::ECR::Repository
    Properties:
      RepositoryName: !Sub "${ProjectName}-${Environment}-ecr"

########################################
# Output
########################################
Outputs:
  RepositoryName:
    Value: !Ref ECR
    Export:
      Name: "RepositoryName"
  RepositoryUri:
    Value: !GetAtt ECR.RepositoryUri
    Export:
      Name: "RepositryUri"
```
</div>
</details>
前記事の手順と同様にECRにイメージプッシュまでのコマンドを実行しておきましょう。

### 3-3. ecs.yml
ECS関連の下記リソースを作成します。
- ECSクラスター
- タスク定義
- ECSサービス
- ECS実行ロール

<details>
<summary>ecs.yml</summary>
<div>

```yml:ecs.yml
AWSTemplateFormatVersion: "2010-09-09"
Description: "Cloudformation template for ECS"

########################################
# Parameters
########################################
Parameters:
  ProjectName:
    Type: "String"
    Description: "Project Name"
  Environment:
    Type: "String"
    AllowedValues:
      - "dev"
      - "stg"
      - "prd"
    Description: "Environment"
  Image:
    Type: "String"
    Description: "Image Name"
  ImageTag:
    Type: "String"
    Description: "Image Tag (e.g. 'latest')"
  DesiredCount:
    Type: "Number"
    Default: 1
    Description: "How many ECS tasks are running"

########################################
# Resources
########################################
Resources:
  # ECSクラスター
  EcsCluster:
    Type: AWS::ECS::Cluster
    Properties:
      ClusterName: !Sub "${ProjectName}-${Environment}-ecs-cluster"

  # タスク定義
  EcsTaskDefinition:
    Type: AWS::ECS::TaskDefinition
    Properties:
      Family: !Sub "${ProjectName}-${Environment}-ecs-task-definition"
      NetworkMode: "awsvpc"
      Cpu: 256
      Memory: 512
      ContainerDefinitions:
        - Name: !Sub "${ProjectName}-${Environment}-ecs-container"
          Image: !Sub "${AWS::AccountId}.dkr.ecr.${AWS::Region}.amazonaws.com/${Image}:${ImageTag}"
      ExecutionRoleArn: !GetAtt EcsTaskExecutionRole.Arn
  ## ECSタスク実行ロール
  EcsTaskExecutionRole:
    Type: AWS::IAM::Role
    Properties:
      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"
      RoleName: !Sub "${ProjectName}-${Environment}-role-ecs-task-exexution"

  # ECSサービス
  EcsService:
    Type: AWS::ECS::Service
    Properties:
      LaunchType: "FARGATE"
      Cluster: !Ref EcsCluster
      TaskDefinition: !Ref EcsTaskDefinition
      ServiceName: !Sub "${ProjectName}-${Environment}-ecs-service"
      DesiredCount: !Ref DesiredCount
      NetworkConfiguration:
        AwsvpcConfiguration:
          AssignPublicIp: "ENABLED"
          SecurityGroups:
            - !ImportValue SecurityGroupId
          Subnets:
            - !ImportValue SubnetId

########################################
# Output
########################################
Outputs:
  ClusterName:
    Value: !Ref EcsCluster
    Export:
      Name: "ClusterName"
  ClusterArn:
    Value: !GetAtt EcsCluster.Arn
    Export:
      Name: "ClusterArn"
  ContainerName:
    Value: !Sub "${ProjectName}-${Environment}-ecs-container"
    Export:
      Name: "ContainerName"
  ServiceName:
    Value: !GetAtt EcsService.Name
    Export:
      Name: "ServiceName"
  ServiceArn:
    Value: !GetAtt EcsService.ServiceArn
    Export:
      Name: "ServiceArn"
```
</div>
</details>

タスクステータスが`RUNNING`になったらパブリックIPにアクセスしindex.htmlが表示されるか確認します。

### 3-4. codecommit.yml
CodeCommitリポジトリを作成します。
<details>
<summary>codecommit.yml</summary>
<div>

```yml:codecommit.yml
AWSTemplateFormatVersion: "2010-09-09"
Description: "Cloudformation template for CodeCommit"

########################################
# Parameters
########################################
Parameters:
  ProjectName:
    Type: "String"
    Description: "Project Name"
  Environment:
    Type: "String"
    AllowedValues:
      - "dev"
      - "stg"
      - "prd"
    Description: "Environment"

########################################
# Resources
########################################
Resources:
  #CodeCommitリポジトリ
  Codecommit:
    Type: AWS::CodeCommit::Repository
    Properties:
      RepositoryName: !Sub "${ProjectName}-${Environment}-codecommit"

########################################
# Output
########################################
Outputs:
  CodeCommitRepositoryName:
    Value: !GetAtt Codecommit.Name
    Export:
      Name: "CodeCommitRepositoryName"
  CodeCommitArn:
    Value: !GetAtt Codecommit.Arn
    Export:
      Name: "CodeCommitArn"
  CodeCommitCloneUrl:
    Value: !GetAtt Codecommit.CloneUrlHttp
    Export:
      Name: "CodeCommitCloneUrl"
```
</div>
</details>

ローカル環境にクローンしてdevelopブランチを切っておきましょう。

### 3-5. s3.yml
アーティファクト保存用のS3バケットをCodeBuild、CodePipeline構築時に指定するため、前もって作成しておきます。
<details>
<summary>s3.yml</summary>
<div>

```yml:s3.yml
AWSTemplateFormatVersion: "2010-09-09"
Description: "Cloudformation template for S3"

########################################
# Parameters
########################################
Parameters:
  ProjectName:
    Type: "String"
    Description: "Project Name"
  Environment:
    Type: "String"
    AllowedValues:
      - "dev"
      - "stg"
      - "prd"
    Description: "Environment"

########################################
# Resources
########################################
Resources:
  # アーティファクト用S3バケット
  S3ArtifactCodePipeline:
    Type: AWS::S3::Bucket
    Properties:
      BucketName: !Sub "${ProjectName}-${Environment}-s3-artifact-codepipeline"
      PublicAccessBlockConfiguration:
        BlockPublicAcls: true
        BlockPublicPolicy: true
        IgnorePublicAcls: true
        RestrictPublicBuckets: true

########################################
# Output
########################################
Outputs:
  S3ArtifactCodePipelineId:
    Value: !Ref S3ArtifactCodePipeline
    Export:
      Name: "S3ArtifactCodePipelineId"
  S3ArtifactCodePipelineArn:
    Value: !GetAtt S3ArtifactCodePipeline.Arn
    Export:
      Name: "S3ArtifactCodePipelineArn"
```
</div>
</details>

### 3-6. codebuild.yml
CodeBuild関連の下記リソースを作成します。
- ビルドプロジェクト
- ビルド実行ロール
- ビルドロググループ

前もってSecretManagerにDockerHubのユーザーネーム、パスワードを保存しておきます。
SecretManagerは管理の安全上、コード化しない方が良いと考えたため、マネジメントコンソールからの保存で進めます。
CloudFormationに渡すパラメーターとしてSecretのIDは使うので控えておきましょう。

またこのタイミングでbuildspec.ymlは作成してdevelopブランチにプッシュしておきます。
<details>
<summary>codebuild.yml</summary>
<div>

```yml:codebuild.yml
AWSTemplateFormatVersion: "2010-09-09"
Description: "Cloudformation template for CodeBuild"

########################################
# Parameters
########################################
Parameters:
  ProjectName:
    Type: "String"
    Description: "Project Name"
  Environment:
    Type: "String"
    AllowedValues:
      - "dev"
      - "stg"
      - "prd"
    Description: "Environment"
  SecretManager:
    Type: String
    Description: "Secret Name after 'secret: ' (e.g. my-secret-xxxxxx)"

########################################
# Resources
########################################
Resources:
  # ビルドプロジェクト
  Codebuild:
    Type: AWS::CodeBuild::Project
    Properties:
        Name: !Sub "${ProjectName}-${Environment}-codebuild"
        Source:
          Type: "CODECOMMIT"
          Location: !ImportValue CodeCommitCloneUrl
          BuildSpec: "./buildspec.yml"
        SourceVersion: "refs/heads/develop"
        Artifacts:
          Type: "no_artifacts"
        Environment:
          Type: "LINUX_CONTAINER"
          ComputeType: "BUILD_GENERAL1_SMALL"
          Image: "aws/codebuild/amazonlinux2-x86_64-standard:4.0"
          PrivilegedMode: true
          EnvironmentVariables:
            - Name: "AWS_ACCOUNT_ID"
              Value: !Sub ${AWS::AccountId}
              Type: "PLAINTEXT"
            - Name: "AWS_DEFAULT_REGION"
              Value: !Sub ${AWS::Region}
              Type: "PLAINTEXT"
            - Name: "IMAGE_REPO_NAME"
              Value: !ImportValue RepositoryName
              Type: "PLAINTEXT"
            - Name: "CONTAINER_NAME"
              Value: !ImportValue ContainerName
              Type: "PLAINTEXT"
        LogsConfig:
          CloudWatchLogs:
            GroupName: !Ref LogGroupCodeBuild
            Status: "ENABLED"
        ServiceRole: !Ref IamRoleCodeBuild
  ## ビルド実行ロール
  IamRoleCodeBuild:
    Type: AWS::IAM::Role
    Properties:
      RoleName: !Sub "${ProjectName}-${Environment}-role-codebuild"
      AssumeRolePolicyDocument:
        Version: "2012-10-17"
        Statement:
          - Effect: "Allow"
            Principal:
              Service:
                - "codebuild.amazonaws.com"
            Action:
              - "sts:AssumeRole"
      ManagedPolicyArns:
        - "arn:aws:iam::aws:policy/AmazonEC2ContainerRegistryPowerUser"
      Policies:
        - PolicyName: !Sub "${ProjectName}-${Environment}-policy-codebuild"
          PolicyDocument:
            Version: "2012-10-17"
            Statement:
              - Effect: "Allow"
                Resource:
                  - !Sub "arn:aws:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/codebuild/${ProjectName}-${Environment}-codebuild"
                  - !Sub "arn:aws:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/codebuild/${ProjectName}-${Environment}-codebuild:*"
                Action:
                  - "logs:CreateLogGroup"
                  - "logs:CreateLogStream"
                  - "logs:PutLogEvents"
              - Effect: "Allow"
                Resource: !Join
                  - ""
                  - - !ImportValue S3ArtifactCodePipelineArn
                    - "*"
                Action:
                  - "s3:PutObject"
                  - "s3:GetObject"
                  - "s3:GetObjectVersion"
                  - "s3:GetBucketAcl"
                  - "s3:GetBucketLocation"
              - Effect: "Allow"
                Resource: !ImportValue CodeCommitArn
                Action: "codecommit:GitPull"
              - Effect: "Allow"
                Resource: !Sub arn:aws:secretsmanager:${AWS::Region}:${AWS::AccountId}:secret:${SecretManager}
                Action: "secretsmanager:GetSecretValue"
  ## ビルドロググループ
  LogGroupCodeBuild:
    Type: AWS::Logs::LogGroup
    Properties:
      LogGroupName: !Sub "/aws/codebuild/${ProjectName}-${Environment}-codebuild"
      RetentionInDays: 30

########################################
# Output
########################################
Outputs:
  CodeBuildProjectName:
    Value: !Ref Codebuild
    Export:
      Name: "CodeBuildProjectName"
  CodeBuildProjectArn:
    Value: !GetAtt Codebuild.Arn
    Export:
      Name: "CodeBuildProjectArn"
```
</div>
</details>
ビルド実行してECRへのイメージプッシュまでの動作確認をします。

### 3-7. codepipeline.yml
CodePipeline関連の下記リソースを作成します。
- CodePipeline
- パイプライン実行ロール
<details>
<summary>codepipeline.yml</summary>
<div>

```yml:codepipeline.yml
AWSTemplateFormatVersion: "2010-09-09"
Description: "Cloudformation template for CodePipeline"

########################################
# Parameters
########################################
Parameters:
  ProjectName:
    Type: "String"
    Description: "Project Name"
  Environment:
    Type: "String"
    AllowedValues:
      - "dev"
      - "stg"
      - "prd"
    Description: "Environment"

########################################
# Resources
########################################
Resources:
  # CodePipeline
  CodePipeline:
    Type: AWS::CodePipeline::Pipeline
    Properties:
      Name: !Sub "${ProjectName}-${Environment}-codepipeline"
      RoleArn: !GetAtt IamRoleCodePipeline.Arn
      ArtifactStore:
        Type: "S3"
        Location: !ImportValue S3ArtifactCodePipelineId
      Stages:
        - Name: "Source"
          Actions:
            - Name: "CodeCommit"
              ActionTypeId:
                Category: "Source"
                Provider: "CodeCommit"
                Owner: "AWS"
                Version: 1
              Configuration:
                RepositoryName: !ImportValue CodeCommitRepositoryName
                BranchName: "develop"
                PollForSourceChanges: false
              OutputArtifacts:
                - Name: "SourceArtifact"
              Namespace: "SourceVariables"
        - Name: "Build"
          Actions:
            - Name: "Build"
              ActionTypeId:
                Category: "Build"
                Provider: "CodeBuild"
                Owner: "AWS"
                Version: 1
              Configuration:
                ProjectName: !ImportValue CodeBuildProjectName
                BatchEnabled: false
              InputArtifacts:
                - Name: "SourceArtifact"
              OutputArtifacts:
                - Name: "BuildArtifact"
              Namespace: "BuildVariables"
        - Name: "Deploy"
          Actions:
            - Name: "Deploy"
              ActionTypeId:
                Category: "Deploy"
                Provider: "ECS"
                Owner: "AWS"
                Version: 1
              Configuration:
                ClusterName: !ImportValue ClusterName
                ServiceName: !ImportValue ServiceName
                FileName: "imagedefinitions.json"
              InputArtifacts:
                - Name: "BuildArtifact"
              Namespace: "DeployVariables"
  ## CodePipeline用ロール
  IamRoleCodePipeline:
    Type: AWS::IAM::Role
    Properties:
      RoleName: !Sub "${ProjectName}-${Environment}-role-codepipeline"
      AssumeRolePolicyDocument:
        Version: "2012-10-17"
        Statement:
          - Effect: "Allow"
            Principal:
              Service:
                - "codepipeline.amazonaws.com"
            Action:
              - "sts:AssumeRole"
      Policies:
        - PolicyName: !Sub "${ProjectName}-${Environment}-policy-codepipeline"
          PolicyDocument:
            Version: "2012-10-17"
            Statement:
              - Effect: "Allow"
                Resource:
                  - !Sub "arn:aws:codepipeline:${AWS::Region}:${AWS::AccountId}:${ProjectName}-${Environment}-codepipeline"
                Action:
                  - "codepipeline:*"
              - Effect: "Allow"
                Resource:
                  - !ImportValue CodeCommitArn
                Action:
                  - "codecommit:CancelUploadArchive"
                  - "codecommit:GetBranch"
                  - "codecommit:GetCommit"
                  - "codecommit:GetRepository"
                  - "codecommit:GetUploadArchiveStatus"
                  - "codecommit:UploadArchive"
              - Effect: "Allow"
                Resource: !Join
                  - ""
                  - - !ImportValue S3ArtifactCodePipelineArn
                    - "*"
                Action:
                  - "s3:PutObject"
                  - "s3:GetObject"
                  - "s3:GetObjectVersion"
                  - "s3:GetBucketAcl"
                  - "s3:GetBucketLocation"
              - Effect: "Allow"
                Resource: !ImportValue CodeBuildProjectArn
                Action:
                  - "codebuild:BatchGetBuilds"
                  - "codebuild:StartBuild"
                  - "codebuild:BatchGetBuildBatches"
                  - "codebuild:StartBuildBatch"
              - Effect: "Allow"
                Resource: "*"
                Action:
                  - "ecs:DescribeServices"
                  - "ecs:DescribeTaskDefinition"
                  - "ecs:DescribeTasks"
                  - "ecs:ListTasks"
                  - "ecs:RegisterTaskDefinition"
                  - "ecs:UpdateService"
                  - "iam:PassRole"

########################################
# Output
########################################
Outputs:
  CodePipelineArn:
    Value: !Sub "arn:aws:codepipeline:${AWS::Region}:${AWS::AccountId}:${ProjectName}-${Environment}-codepipeline"
    Export:
      Name: "CodePipelineArn"
```
</div>
</details>
構築完了後、初回起動されるのでデプロイステージが完了するまで待ちます。

### 3-8. eventbridge.yml
最後にAmazon EventBridgeを構築し、パイプラインの自動起動を確認します。
EventBridge関連の下記リソースを作成します。
- イベントルール
- EventBridge実行ロール
<details>
<summary>eventbridge.yml</summary>
<div>

```yml:eventbridge.yml
AWSTemplateFormatVersion: "2010-09-09"
Description: "Cloudformation template for Event Bridge"

########################################
# Parameters
########################################
Parameters:
  ProjectName:
    Type: "String"
    Description: "Project Name"
  Environment:
    Type: "String"
    AllowedValues:
      - "dev"
      - "stg"
      - "prd"
    Description: "Environment"

########################################
# Resources
########################################
Resources:
  ## Event Bridge
  EventsRuleCodePipeline:
    Type: AWS::Events::Rule
    Properties:
      Name: !Sub "${ProjectName}-${Environment}-event-rule-codepipeline"
      EventPattern:
        source:
          - "aws.codecommit"
        detail-type:
          - "CodeCommit Repository State Change"
        resources:
          - !ImportValue CodeCommitArn
        detail:
          event:
            - "referenceCreated"
            - "referenceUpdated"
          referenceType:
            - "branch"
          referenceName:
            - "develop"
      Targets:
        - Arn: !ImportValue CodePipelineArn
          Id: !Sub "${ProjectName}-${Environment}-event-rule-codepipeline"
          RoleArn: !GetAtt IamRoleEventRule.Arn
  ## Event Bridge用ロール
  IamRoleEventRule:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: "2012-10-17"
        Statement:
          - Effect: "Allow"
            Principal:
              Service:
                - "events.amazonaws.com"
            Action:
              - "sts:AssumeRole"
      Policies:
        - PolicyName: !Sub "${ProjectName}-${Environment}-policy-event-rule-codepipeline"
          PolicyDocument:
            Version: "2012-10-17"
            Statement:
              - Effect: "Allow"
                Resource:
                  - !ImportValue CodePipelineArn
                Action:
                  - "codepipeline:StartPipelineExecution"
      RoleName: !Sub "${ProjectName}-${Environment}-role-event-rule-codepipeline"
```
</div>
</details>
ソースコードを更新してdevelopブランチにプッシュし、CodePipelineが自動起動するかを確認します。

# まとめ
今回はECRの作成からCodePipelineの作成までをコード化してCloudFormationで構築する流れをまとめました。

マネジメントコンソールでリソース構築する際はIAMロール(ポリシー)など、ある程度AWS側で自動作成してくれますが、必要以上の権限が自動的に付与されていることがあります。
コード化する場合は、構築後の拡張性を持たせてあらかじめ権限範囲をある程度持たせておく等、そういった権限周りも自身で設計する必要がありますので難しいですね。
パラメーターに与えている内容等もより管理のしやすいように見直しが必要かもしれません。

前の記事はこちら
[【AWS】 (初心者向け) CodePipelineを使ったECSコンテナのデプロイ自動化 その1(ECR, ECS Fargate)](https://qiita.com/ramunauna/items/6417925934441b9fdc78)
[【AWS】 (初心者向け) CodePipelineを使ったECSコンテナのデプロイ自動化 その2(CodeCommit, CodeBuild, CodePipeline)](https://qiita.com/ramunauna/items/114ffb3b5532cdb7fd2d)