CodePipelineでECSのBlue/Greenデプロイをする

AWS ECS上コンテナのBlue/Greenデプロイメントを、Code Pipelineから実施します。CodeCommitにあるDokcerfileを含むソースコードを、CodeBuildでdocker buildし、CodeDeployにてBlue/Greenする流れです。

環境用意

まず必要となる環境を用意していきます。

Dockerfileの用意

Nginxを利用する簡単なDockerfileを用意します。

フォルダ構造

.
├── Dockerfile
└── file
    └── index.html

Dockerfile

FROM nginx:latest
COPY ./file/index.html /usr/share/nginx/html/

file/index.html

<html>
  <head>
    <title>sample</title>
  </head>
  <body>
    sample 1.0
  </body>
</html>

ECRリポジトリの用意

今回利用するECRリポジトリを作成します。作成するリポジトリ情報を定義したJSONファイルを用意します。

ecr-repository.json

{
  "repositoryName": "deploy-nginx",
  "tags": [
    {
      "Key": "key-string",
      "Value": "value-string"
    }
  ],
  "imageTagMutability": "MUTABLE",
  "imageScanningConfiguration": {
    "scanOnPush": false
  }
}

--cli-input-json オプションにて、リポジトリを作成します。

$ aws ecr create-repository --cli-input-json file://ecr-repository.json

aws ecr create-repository

先程用意したDockerfileをbuildします。

$ docker build -t 123456789012.dkr.ecr.ap-northeast-1.amazonaws.com/deploy-nginx:latest .

ECRへのログイン処理を実施。

$ (aws ecr get-login --no-include-email)

ECRにpushします。

$ docker push 123456789012.dkr.ecr.ap-northeast-1.amazonaws.com/deploy-nginx:latest

ECSの用意

ECS環境を用意します。具体的作業は割愛。

以下に続く例では、 sample-ecs-cluster という名前のECS Clusterを作成しています。

利用するALBの用意

ECS上のコンテナで利用されるALBを用意します。以下のような構成のALBを用意します。

  • ALB
    • 80/TCPのリスナー
    • 80/TCPのリスナーで利用されるターゲットグループ
    • 8080/TCPのリスナー
    • 8080/TCPのリスナーで利用されるターゲットグループ
  • いずれもHTTP通信

作成してくれるCloudFormationテンプレートを置いておきます。

https://github.com/goodbyegangster/cloudformation/tree/master/cf-alb-for-ecs-blue-green

ECSタスク定義の作成

ECRにpushしたコンテナイメージを利用して、ECSタスク定義を作成します。

task-definition-deploy-nginx.json

{
  "family": "deploy-nginx",
  "taskRoleArn": "arn:aws:iam::123456789012:role/xxxxxxxxxx",
  "executionRoleArn": "arn:aws:iam::123456789012:role/ecsTaskExecutionRole",
  "networkMode": "awsvpc",
  "containerDefinitions": [
    {
      "name": "deploy-nginx",
      "image": "123456789012.dkr.ecr.ap-northeast-1.amazonaws.com/deploy-nginx:latest",
      "cpu": 1024,
      "memory": 512,
      "portMappings": [
        {
          "containerPort": 80,
          "hostPort": 80,
          "protocol": "tcp"
        }
      ],
      "essential": true
    }
  ],
  "requiresCompatibilities": [
    "EC2"
  ],
  "tags": [
    {
      "key": "tag-key",
      "value": "tag-value"
    }
  ]
}

タスクを登録します。

$ aws ecs register-task-definition --cli-input-json file://task-definition-deploy-nginx

aws ecs register-task-definition

サービスの作成

登録したタスク定義を利用してサービスを起動します。

上記で登録したタスク定義を利用して、サービスを起動します。

service-definition-deploy-nginx.json

{
    "taskDefinition": "deploy-nginx:1",
    "cluster": "sample-ecs-cluster",
    "loadBalancers": [
        {
            "targetGroupArn": "arn:aws:elasticloadbalancing:ap-northeast-1:123456789012:targetgroup/alb-for-ecs-target-group-1/c1f2a3d2bdae2b3f",
            "containerName": "deploy-nginx",
            "containerPort": 80
        }
    ],
    "desiredCount": 1,
    "launchType": "EC2",
    "schedulingStrategy": "REPLICA",
    "deploymentController": {
        "type": "CODE_DEPLOY"
    },
    "networkConfiguration": {
        "awsvpcConfiguration": {
            "subnets": [
                "subnet-xxxxx",
                "subnet-xxxxx"
            ],
            "securityGroups": [
                "sg-xxxxxxx"
            ],
            "assignPublicIp": "DISABLED"
        }
    }
}

サービスを作成します。

$ aws ecs create-service --service-name service-deploy-nginx --cli-input-json file://service-definition-deploy-nginx.json

aws ecs create-service

コンテナ起動後、ALBのエンドポイントにアクセスして、WEBサイトの表示を確認します。

$ curl http://alb-for-ecs-1691494987.ap-northeast-1.elb.amazonaws.com/
<html>
  <head>
    <title>sample</title>
  </head>
  <body>
    sample 1.0
  </body>
</html>

環境の準備は完了です。

CodePipeline系の設定

ここから、Blue/Greenデプロイのための各Code系サービスの設定をしていきます。

イメージ図

色んなサービス出てきて分かりにくいですが、こんな感じのイメージ図になります。

f:id:goodbyegangster:20200209082312p:plain

CodeCommitにてGitリポジトリの作成

CodeCommitにてGitリポジトリを用意します。作成方法は、過去の投稿内容を参考。

WindowsでCodeCommitを使ってみた話

CodePipelineでArtifactを配置するS3バケットを用意

CodePipelineの処理で各Artifactを配置するS3バケットを作成します。

CodeBuildプロジェクトの作成

ビルド処理にて利用されるCodeBuildのプロジェクトを作成します。今回の例では、以下のCloudFormationテンプレートにて、プロジェクトを作成します。

AWSTemplateFormatVersion: "2010-09-09"
Description: "CFn for CodeBuild(docker build from Codecommit)"
Metadata:
  AWS::CloudFormation::Interface:
    ParameterGroups:
      -
        Label:
          default: "CFn for CodeBuild(docker build from Codecommit)"
        Parameters:
          - ProjectName
          - ArtifactS3Location

Parameters:
  ProjectName:
    Type: "String"
  ArtifactS3Location:
    Type: "String"

Resources:
  LogGroup:
    Type: "AWS::Logs::LogGroup"
    # https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/aws-resource-logs-loggroup.html
    DeletionPolicy: "Delete"
    Properties:
      LogGroupName: !Sub /aws/codebuild/${ProjectName}
      RetentionInDays: 30
  LogStream:
    Type: "AWS::Logs::LogStream"
    # https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/aws-resource-logs-logstream.html
    DeletionPolicy: "Delete"
    DependsOn: "LogGroup"
    Properties:
      LogGroupName: !Ref LogGroup
      LogStreamName: "sample"
  IAMRole:
    Type: "AWS::IAM::Role"
    # https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/aws-resource-iam-role.html
    DeletionPolicy: "Delete"
    DependsOn: "LogStream"
    Properties:
      AssumeRolePolicyDocument:
        Version: "2012-10-17"
        Statement:
          -
            Effect: "Allow"
            Principal:
              Service: "codebuild.amazonaws.com"
            Action: "sts:AssumeRole"
      # Description: String
      # ManagedPolicyArns:
      #   - String
      # MaxSessionDuration: Integer
      Path: "/"
      # PermissionsBoundary: String
      Policies:
        - PolicyName: "cloudwatchlogs"
          PolicyDocument:
            Version: "2012-10-17"
            Statement:
              -
                Effect: "Allow"
                Action:
                  - "logs:CreateLogGroup"
                  - "logs:CreateLogStream"
                  - "logs:PutLogEvents"
                Resource:
                  - !Sub arn:aws:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/codebuild/${ProjectName}
                  - !Sub arn:aws:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/codebuild/${ProjectName}:*
        - PolicyName: "ecr"
          PolicyDocument:
            Version: "2012-10-17"
            Statement:
              -
                Effect: "Allow"
                Action:
                  - "ecr:GetAuthorizationToken"
                  - "ecr:BatchCheckLayerAvailability"
                  - "ecr:GetDownloadUrlForLayer"
                  - "ecr:BatchGetImage"
                  - "ecr:PutImage"
                  - "ecr:InitiateLayerUpload"
                  - "ecr:UploadLayerPart"
                  - "ecr:CompleteLayerUpload"
                Resource:
                  - "*"
        - PolicyName: "codebuild"
          PolicyDocument:
            Version: "2012-10-17"
            Statement:
              -
                Effect: "Allow"
                Action:
                  - "codebuild:CreateReportGroup"
                  - "codebuild:CreateReport"
                  - "codebuild:UpdateReport"
                  - "codebuild:BatchPutTestCases"
                Resource:
                  - !Sub arn:aws:codebuild:${AWS::Region}:${AWS::AccountId}:report-group/${ProjectName}-*
        - PolicyName: "s3"
          PolicyDocument:
            Version: "2012-10-17"
            Statement:
              -
                Effect: "Allow"
                Action:
                  - "s3:PutObject"
                  - "s3:GetObject"
                  - "s3:GetObjectVersion"
                  - "s3:GetBucketAcl"
                  - "s3:GetBucketLocation"
                Resource:
                  - !Sub arn:aws:s3:::${ArtifactS3Location}
                  - !Sub arn:aws:s3:::${ArtifactS3Location}/*
      RoleName: !Sub code-build-${ProjectName}-service-role
      # Tags:
      #   - Tag
  Project:
    Type: "AWS::CodeBuild::Project"
    # https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/aws-resource-codebuild-project.html
    DeletionPolicy: "Delete"
    DependsOn: "IAMRole"
    Properties:
      Artifacts:
        # ArtifactIdentifier: String
        # EncryptionDisabled: Boolean
        # Location: String
        # Name: String
        # NamespaceType: String
        # OverrideArtifactName: Boolean
        # Packaging: String
        # Path: String
        Type: "CODEPIPELINE"
      BadgeEnabled: false
      # Cache:
      #   Location: String
      #   Modes:
      #     - String
      #   Type: String
      Description: "sample"
      # EncryptionKey: String
      Environment:
        # Certificate: String
        ComputeType: "BUILD_GENERAL1_SMALL"
        # EnvironmentVariables:
        #   -
        #     Name: String
        #     Type: String
        #     Value: String
        Image: "aws/codebuild/amazonlinux2-x86_64-standard:2.0"
        ImagePullCredentialsType: "CODEBUILD"
        PrivilegedMode: "true"
        # RegistryCredential: 
        #   RegistryCredential
        Type: "LINUX_CONTAINER"
      LogsConfig:
        CloudWatchLogs:
          GroupName: !Ref LogGroup
          Status: "ENABLED"
          StreamName: !Ref LogStream
        # S3Logs:
        #   EncryptionDisabled: Boolean
        #   Location: String
        #   Status: String
      Name: !Ref ProjectName
      # QueuedTimeoutInMinutes: Integer
      # SecondaryArtifacts:
      #   - Artifacts
      # SecondarySources:
      #   - Source
      # SecondarySourceVersions: 
      #   - ProjectSourceVersion
      ServiceRole: !Ref IAMRole
      Source:
        # Auth:
        #   SourceAuth
        BuildSpec: "configuration/buildspec.yml"
        GitCloneDepth: 1
        # GitSubmodulesConfig:
        #   FetchSubmodules: Boolean
        # InsecureSsl: Boolean
        # Location: String
        # ReportBuildStatus: Boolean
        # SourceIdentifier: String
        Type: "CODEPIPELINE"
      # SourceVersion: String
      Tags: 
        - Key: "key-string"
          Value: "value-string"
      # TimeoutInMinutes: Integer
      # Triggers: 
      #   ProjectTriggers
      # VpcConfig: 
      #   VpcConfig

CodeDeployの設定

CodeDeploy用の設定を実施します。

ECS Blue/Greenデプロイ用のアプリケーションを作成します。

$ aws deploy create-application \
--application-name application-deploy-nginx \
--compute-platform ECS

aws deploy create-application

上記で作成したアプリケーションに紐付けるデプロイグループを作成します。設定情報を定義したJSONファイルを用意します。

dep-group-deploy-nginx.json

{
  "applicationName": "application-sample-nginx",
  "deploymentGroupName": "dep-group-deploy-nginx",
  "deploymentConfigName": "CodeDeployDefault.ECSAllAtOnce",
  "serviceRoleArn": "arn:aws:iam::123456789012:role/ecsCodeDeployRole",
  "alarmConfiguration": {
    "enabled": false,
    "ignorePollAlarmFailure": false,
    "alarms": []
  },
  "autoRollbackConfiguration": {
    "enabled": true,
    "events": [
      "DEPLOYMENT_STOP_ON_ALARM",
      "DEPLOYMENT_FAILURE",
      "DEPLOYMENT_STOP_ON_REQUEST"
    ]
  },
  "deploymentStyle": {
    "deploymentType": "BLUE_GREEN",
    "deploymentOption": "WITH_TRAFFIC_CONTROL"
  },
  "blueGreenDeploymentConfiguration": {
    "terminateBlueInstancesOnDeploymentSuccess": {
      "action": "TERMINATE",
      "terminationWaitTimeInMinutes": 30
    },
    "deploymentReadyOption": {
      "actionOnTimeout": "CONTINUE_DEPLOYMENT",
      "waitTimeInMinutes": 0
    }
  },
  "loadBalancerInfo": {
    "targetGroupPairInfoList": [
      {
        "targetGroups": [
          {
            "name": "alb-for-ecs-target-group-1"
          },
          {
            "name": "alb-for-ecs-target-group-2"
          }
        ],
        "prodTrafficRoute": {
          "listenerArns": [
            "arn:aws:elasticloadbalancing:ap-northeast-1:123456789012:listener/app/alb-for-ecs/d1d4aaaade0ff15d/5f556d2bd52f823e"
          ]
        },
        "testTrafficRoute": {
          "listenerArns": [
            "arn:aws:elasticloadbalancing:ap-northeast-1:123456789012:listener/app/alb-for-ecs/d1d4aaaade0ff15d/33812e2854b60177"
          ]
        }
      }
    ]
  },
  "ecsServices": [
    {
      "serviceName": "service-deploy-nginx",
      "clusterName": "sample-ecs-cluster"
    }
  ],
  "tags": [
    {
      "Key": "tag-key",
      "Value": "tag-value"
    }
  ]
}

--cli-input-json を利用して定義を流し込んで、デプロイグループを作成します。

$ aws deploy create-deployment-group \
--application-name application-deploy-nginx \
--deployment-group-name dep-group-deploy-nginx \
--cli-input-json file://dep-group-deploy-nginx.json

aws deploy create-deployment-group

appspecファイルの作成

appspecファイルを用意します。appspecファイル(application specification file)とは、CodeDeploy処理時に参照されるアプリケーション情報やデプロイ時のHook処理を定義しておくファイルです。

CodePipelineからECSをデプロイする場合には、 TaskDefinition というパラメータに <TASK_DEFINITION> という値を入れてあげます。こうしておくことで、CodePipeline内の処理にて利用されるタスク定義の設定してくれるようなります。

appspec.yml

version: 0.0
Resources:
  - TargetService:
      Type: AWS::ECS::Service
      Properties:
        TaskDefinition: <TASK_DEFINITION>
        LoadBalancerInfo:
          ContainerName: "deploy-nginx"
          ContainerPort: 80
# Optional properties
#         PlatformVersion: "LATEST"
#         NetworkConfiguration:
#           AwsvpcConfiguration:
#             Subnets: ["subnet-xxxxxx","subnet-xxxxxx"]
#             SecurityGroups: ["sg-xxxxxxxx"]
#             AssignPublicIp: "DISABLED"
# Hooks:
#   - BeforeInstall: "LambdaFunctionToValidateBeforeInstall"
#   - AfterInstall: "LambdaFunctionToValidateAfterTraffic"
#   - AfterAllowTestTraffic: "LambdaFunctionToValidateAfterTestTrafficStarts"
#   - BeforeAllowTraffic: "LambdaFunctionToValidateBeforeAllowingProductionTraffic"
#   - AfterAllowTraffic: "LambdaFunctionToValidateAfterAllowingProductionTraffic"

Amazon ECS のデプロイ向けの AppSpec File の例

taskdefファイルの作成

デプロイ対象となるECSタスク定義ファイルを抽出してきます。上記のタスク定義を登録した時にタスク定義用JSONファイルを作成していますが、より正確な情報を取得するため、現在登録されている情報をエクスポートしてきます。

$ aws ecs describe-task-definition --task-definition deploy-nginx | jq .taskDefinition > taskdef.json

aws ecs describe-task-definition

作成したtaskdef.jsonファイルより、パラメータ image の値を "<IMAGE1_NAME>" に変更します。こうすることで、CodePipelineの処理にて、利用されるタスク定義を更新してくれます。

taskdef.json

...
        "name": "deploy-nginx",
        "image": "<IMAGE1_NAME>",
        "cpu": 1024,
...

buildspecファイルの作成

CodeBuildのビルド処理で実行される内容を定義したbuildspec.ymlファイルを作成します。注意点は下記。

利用するECRのリポジトリを、 REPOSITORY_URI で指定します。

本手順でのCodeBuildのビルド環境は、Amazon Linux2を利用しています。以下のドキュメントに記載ある通り、Amazon Linux2を利用する場合、ビルドしたいアプリケーションに合わせてランタイムを指定する必要があります。docker buildしたい場合、buildspecファイル内で docker: 18 とランタイム・バージョンを指定する必要があります。

Ubuntu 18.04 および Amazon Linux 2 プラットフォームの標準イメージには、以下のランタイムが含まれています。Amazon Linux 2 標準イメージ 1.0 または Ubuntu 標準イメージ 2.0 を使用する場合、buildspec ファイルの runtime-versions セクションでランタイムを指定する必要があります。

CodeBuild に用意されている Docker イメージ

CodePipelineでECSのデプロイを実施する場合、イメージ定義ドキュメントをデプロイ時のSource Artifactに作成する必要があります。イメージ定義ファイルとは、デプロイするコンテナイメージ等を定義した設定ファイルとなっています。CodePipelineでBlue/Greenデプロイする場合、イメージ定義ファイルは imageDetail.json という名前にする必要があり、定義方法も定められています。詳細は下記資料にて。本手順では、デプロイ時に利用するコンテナイメージはCodeBuildのビルド処理により作成されることになるので、コンテナイメージの情報を記載したイメージ定義ファイルをbuildspecファイル内で作成しています。

Amazon ECS Blue/Green デプロイアクション用 imageDetail.json ファイル

buildspec.yml

version: 0.2
phases:
  install:
    runtime-versions:
      docker: 18
  pre_build:
    commands:
      - echo Logging in to Amazon ECR...
      - aws --version
      - $(aws ecr get-login --region $AWS_DEFAULT_REGION --no-include-email)
      - REPOSITORY_URI=123456789012.dkr.ecr.ap-northeast-1.amazonaws.com/deploy-nginx
      - COMMIT_HASH=$(echo $CODEBUILD_RESOLVED_SOURCE_VERSION | cut -c 1-7)
      - IMAGE_TAG=${COMMIT_HASH:=latest}
  build:
    commands:
      - echo Build started on `date`
      - echo Building the Docker image...
      - docker build -t $REPOSITORY_URI:latest .
      - docker tag $REPOSITORY_URI:latest $REPOSITORY_URI:$IMAGE_TAG
  post_build:
    commands:
      - echo Build completed on `date`
      - echo Pushing the Docker images...
      - docker push $REPOSITORY_URI:latest
      - docker push $REPOSITORY_URI:$IMAGE_TAG
      - echo Writing image detail file...
      - printf '{"ImageURI":"%s"}' $REPOSITORY_URI:$IMAGE_TAG > imageDetail.json
artifacts:
    files: imageDetail.json

CodeCommitへのgit push

最終的に、これまで作成したファイルを下記のような構成に配置して、Codecommit上に作ったgitリポジトリにpushします。

.
├── Dockerfile
├── configuration
│   ├── appspec.yml
│   ├── buildspec.yml
│   └── taskdef.json
└── file
      └── index.html

CodePipelineの設定

CodePipelineの設定を実施。今回は以下のCloudFormationテンプレートで作成しました。

各ステージで利用できるアクションタイプ(SourceとかBuildとか)や、アクションプロバイダ(CodeCommitとかCodeBuildとか)、各アクションプロバイダで指定するConfiguration情報は、下記ページにて確認できます。基本的に、各ステージで利用される設定や、ステージ間で連携されるArtifactに関する情報を定義することになります。

CodePipeline パイプライン構造のリファレンス

AWSTemplateFormatVersion: "2010-09-09"
Description: "CFn for Code Pipeline(ECS Blue/Green)"
Metadata:
  AWS::CloudFormation::Interface:
    ParameterGroups:
      -
        Label:
          default: "CFn for Code Pipeline(ECS Blue/Green)"
        Parameters:
          - PipeLineName
          - ArtifactS3Location
          - CodeCommitRepoName
          - CodeBuildProjectName
          - CodeDeployAppName
          - CodeDeployDepName
Parameters:
  PipeLineName:
    Type: "String"
  ArtifactS3Location:
    Type: "String"
  CodeCommitRepoName:
    Type: "String"
  CodeBuildProjectName:
    Type: "String"
  CodeDeployAppName:
    Type: String
  CodeDeployDepName:
    Type: String

Resources:
  CloudwatchIAMRole:
    Type: "AWS::IAM::Role"
    # https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/aws-resource-iam-role.html
    DeletionPolicy: "Delete"
    Properties:
      AssumeRolePolicyDocument:
        Version: "2012-10-17"
        Statement:
          -
            Effect: "Allow"
            Principal:
              Service:
                - "events.amazonaws.com"
            Action:
              - "sts:AssumeRole"
      # Description: String
      # ManagedPolicyArns:
      #   - String
      # MaxSessionDuration: Integer
      Path: "/"
      # PermissionsBoundary: String
      Policies:
        - PolicyName: "codepipeline"
          PolicyDocument:
            Version: "2012-10-17"
            Statement:
              -
                Effect: "Allow"
                Action:
                  - "codepipeline:StartPipelineExecution"
                Resource:
                  - !Sub arn:aws:codepipeline:${AWS::Region}:${AWS::AccountId}:${PipeLineName}
      RoleName: !Sub cloudwatch-event-rule-for-code-pipeline-${PipeLineName}
      # Tags:
      #   - Tag
  CloudwatchEventRule:
    Type: "AWS::Events::Rule"
    # https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/aws-resource-events-rule.html
    DeletionPolicy: "Delete"
    DependsOn: "CloudwatchIAMRole"
    Properties:
      Description: !Sub Codepipeline-${PipeLineName}
      EventPattern:
        source:
          - aws.codecommit
        detail-type:
          - "CodeCommit Repository State Change"
        resources:
          - !Sub arn:aws:codecommit:${AWS::Region}:${AWS::AccountId}:${CodeCommitRepoName}
        detail:
          event:
            - referenceCreated
            - referenceUpdated
          referenceType:
            - branch
          referenceName:
            - master
      Name: !Sub rule-codepipeline-${PipeLineName}
      # RoleArn: String
      # ScheduleExpression: String
      State: "ENABLED"
      Targets: 
        -
          Arn: !Sub arn:aws:codepipeline:${AWS::Region}:${AWS::AccountId}:${PipeLineName}
          # EcsParameters: 
          #   EcsParameters
          Id: !Sub id-codepipeline-${PipeLineName}
          # Input: String
          # InputPath: String
          # InputTransformer: 
          #   InputTransformer
          # KinesisParameters: 
          #   KinesisParameters
          RoleArn: !GetAtt CloudwatchIAMRole.Arn
          # RunCommandParameters: 
          #   RunCommandParameters
          # SqsParameters: 
          #   SqsParameters
  PipeLineIAMRole:
    Type: "AWS::IAM::Role"
    # https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/aws-resource-iam-role.html
    DeletionPolicy: "Delete"
    DependsOn: "CloudwatchEventRule"
    Properties:
      AssumeRolePolicyDocument:
        Version: "2012-10-17"
        Statement:
          -
            Effect: "Allow"
            Principal:
              Service:
                - "codepipeline.amazonaws.com"
            Action:
              - "sts:AssumeRole"
      # Description: String
      # ManagedPolicyArns:
      #   - String
      # MaxSessionDuration: Integer
      Path: "/"
      # PermissionsBoundary: String
      Policies: 
        - PolicyName: "iam"
          PolicyDocument:
            Version: "2012-10-17"
            Statement:
              -
                Effect: "Allow"
                Action:
                  - "iam:PassRole"
                Resource:
                  - "*"
                Condition:
                  StringEqualsIfExists:
                    iam:PassedToService:
                      - "ecs-tasks.amazonaws.com"
        - PolicyName: "codecommit"
          PolicyDocument:
            Version: "2012-10-17"
            Statement:
              -
                Effect: "Allow"
                Action:
                  - "codecommit:CancelUploadArchive"
                  - "codecommit:GetBranch"
                  - "codecommit:GetCommit"
                  - "codecommit:GetUploadArchiveStatus"
                  - "codecommit:UploadArchive"
                Resource:
                  - "*"
        - PolicyName: "ecr"
          PolicyDocument:
            Version: "2012-10-17"
            Statement:
              -
                Effect: "Allow"
                Action:
                  - "ecr:DescribeImages"
                Resource:
                  - "*"
        - PolicyName: "codebuild"
          PolicyDocument:
            Version: "2012-10-17"
            Statement:
              -
                Effect: "Allow"
                Action:
                  - "codebuild:BatchGetBuilds"
                  - "codebuild:StartBuild"
                Resource:
                  - "*"
        - PolicyName: "codedeploy"
          PolicyDocument:
            Version: "2012-10-17"
            Statement:
              -
                Effect: "Allow"
                Action:
                  - "codedeploy:CreateDeployment"
                  - "codedeploy:GetApplication"
                  - "codedeploy:GetApplicationRevision"
                  - "codedeploy:GetDeployment"
                  - "codedeploy:GetDeploymentConfig"
                  - "codedeploy:RegisterApplicationRevision"
                Resource:
                  - "*"
        - PolicyName: "other"
          PolicyDocument:
            Version: "2012-10-17"
            Statement:
              -
                Effect: "Allow"
                Action:
                  - "elasticloadbalancing:*"
                  - "cloudwatch:*"
                  - "s3:*"
                  - "ecs:*"
                  - "sns:*"
                  - "cloudformation:*"
                Resource:
                  - "*"
      RoleName: !Sub code-pipeline-${PipeLineName}-service-role
      # Tags:
      #   - Tag
  PipeLine:
    Type: "AWS::CodePipeline::Pipeline"
    # https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/aws-resource-codepipeline-pipeline.html
    DeletionPolicy: "Delete"
    DependsOn: "PipeLineIAMRole"
    Properties:
      ArtifactStore:
        # EncryptionKey:
        #   EncryptionKey
        Location: !Ref ArtifactS3Location
        Type: "S3"
      # ArtifactStores: 
      #   - ArtifactStoreMap
      # DisableInboundStageTransitions: 
      #   - StageName: String
      #     Reason: String
      Name: !Ref PipeLineName
      RestartExecutionOnUpdate: false
      RoleArn: !GetAtt PipeLineIAMRole.Arn
      Stages:
        - Name: "Source"
          Actions:
            - Name: "Source"
              ActionTypeId:
                Category: "Source"
                Owner: "AWS"
                Provider: "CodeCommit"
                Version: "1"
              Configuration:
                RepositoryName: !Ref CodeCommitRepoName
                BranchName: "master"
                PollForSourceChanges: "false"
              # InputArtifacts: 
              #   - Name: String
              Name: "Source"
              OutputArtifacts:
                - Name: "SourceArtifact"
              Region: !Ref AWS::Region
              # RoleArn: String
              RunOrder: 1
        - Name: "Build"
          Actions:
            - Name: "Build"
              ActionTypeId:
                Category: "Build"
                Owner: "AWS"
                Provider: "CodeBuild"
                Version: "1"
              Configuration:
                ProjectName: !Ref CodeBuildProjectName
              InputArtifacts: 
                - Name: "SourceArtifact"
              Name: "Build"
              OutputArtifacts:
                - Name: "BuildArtifact"
              Region: !Ref AWS::Region
              # RoleArn: String
              RunOrder: 1
        - Name: "Deploy"
          Actions:
            - Name: "Deploy"
              ActionTypeId:
                Category: "Deploy"
                Owner: "AWS"
                Provider: "CodeDeployToECS"
                Version: "1"
              Configuration:
                ApplicationName: !Ref CodeDeployAppName
                DeploymentGroupName: !Ref CodeDeployDepName
                Image1ArtifactName: "BuildArtifact"
                Image1ContainerName: "IMAGE1_NAME"
                TaskDefinitionTemplateArtifact: "SourceArtifact"
                TaskDefinitionTemplatePath: "configuration/taskdef.json"
                AppSpecTemplateArtifact: "SourceArtifact"
                AppSpecTemplatePath: "configuration/appspec.yml"
              InputArtifacts:
                - Name: "SourceArtifact"
                - Name: "BuildArtifact"
              Name: "Deploy"
              # OutputArtifacts:
              #   - Name: String
              Region: !Ref AWS::Region
              # RoleArn: String
              RunOrder: 1
          # Blockers:
          #   - BlockerDeclaration

パイプラインが作成されると、初回にテスト実行が走ります。問題ない場合、全処理が正常終了します。

今後、CodeCommit側のリポジトリがコミットされる都度、本パイプラインが流れることになります。