異なるAWSアカウントでBlue/Green自動デプロイする（クロスアカウントデプロイ@ECS Fargate）

マルチアカウント環境で`AWS Fargate`でBlue/Greenの自動デプロイを設定します（開発環境->本番環境へのデプロイを想定）。
ソースレポジトリには`GitHub`、ビルドには`CodeBuild`、デプロイパイプラインには`CodePipeline`を用います。
自動ビルドの設定は、`CloudFormation`とマネジメントコンソールを利用します。

※以下の手順を実行すると、`AWS`のサービス利用料が発生します。
※開発環境（デプロイ元環境）、本番環境（デプロイ先環境）の前提で記載します。
※開発環境（デプロイ元環境）、本番環境（デプロイ先環境）のBlue/Greenデプロイ設定は完了しているものとします。
※`Fargate`のBlue/Greenデプロイ設定は、以下を参照してください。
[ECS FargateでBlue/Greenデプロイ設定](https://qiita.com/takeshi_hirosue/items/0ccacbe8015b3830e1a7 "ECS FargateでBlue/Greenデプロイ設定")

※開発環境（デプロイ元環境）のBlue/Green自動デプロイ設定は完了しているものとします。
※`Fargate`のBlue/Green自動デプロイは、以下を参照してください。
[ECS FargateでGitHubのWebHookでBlue/Green自動デプロイする](https://qiita.com/takeshi_hirosue/items/19901da776c3f301f7cf "ECS FargateでGitHubのWebHookでBlue/Green自動デプロイする")

※今回のサンプルは以下を利用しています。
https://github.com/Thirosue/cross-accout-deploy-sample

### 構成イメージ

今回の構成のイメージは下図のとおりです。

<img width="819" alt="スクリーンショット 2020-01-18 15.49.35.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/73176/dc9c6f25-5217-08c1-f250-a6841c83dfcc.png">

## 1. 本番環境（デプロイ先環境）の前準備

ECR Pushを許可するロールおよびデプロイ設定ファイルを配置する`S3`へのアクセスを許可するロールを作成（開発環境->本番環境）します。
->以下`yaml`を`AWS CLI`かマネジメントコンソールで実行します(`CloudFormation`)。

```:yaml
  # ------------------------------------------------------------#
  #  Cross Account Role For Develop Account
  # ------------------------------------------------------------#
  ECRRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: '2012-10-17'
        Statement:
        - Effect: Allow
          Principal:
            AWS: !Sub arn:aws:iam::${TargetAccountId}:root #開発環境のアカウントIDをParameterに設定
          Action: sts:AssumeRole 
      ManagedPolicyArns:
        - arn:aws:iam::aws:policy/AmazonEC2ContainerRegistryFullAccess #必要に応じ権限を絞る
      RoleName: auto-deploy-for-fargate

  # ------------------------------------------------------------#
  #  Bucket For DeploySetting 
  # ------------------------------------------------------------#
  ECSDeploySettingBucket:
    Type: AWS::S3::Bucket
    Properties:
      BucketName: !Sub ${Env}-${ServiceName}-auto-deploy
      VersioningConfiguration:
        Status: Enabled
  ECSDeploySettingBucketPolicy:
    Type: AWS::S3::BucketPolicy
    Properties:
      Bucket: !Ref ECSDeploySettingBucket
      PolicyDocument:
        Id: "Allow-Cross-Account-Access"
        Version: '2012-10-17'
        Statement:
          - Sid: CrossAccountBucketPolicy
            Effect: Allow
            Principal:
              AWS: !Sub arn:aws:iam::${TargetAccountId}:root #開発環境のアカウントIDをParameterに設定
            Action:
              - s3:*
            Resource: !Sub arn:aws:s3:::${Env}-${ServiceName}-auto-deploy/*
```

※設定ファイルの全量は以下を参照してください。
https://github.com/Thirosue/cross-accout-deploy-sample/blob/master/provisioning/prod/CrossAccountDeployForFargate.yaml

## 2. 開発環境（デプロイ元環境）の追加設定（ビルド/デプロイ）

`CodeBuild`に以下を追加し、本番環境にコンテナイメージおよびデプロイ設定ファイルを連携します。
->ユーザスイッチせずにPushすると`CodePipeline`がトリガーされないため、アカウントをスイッチします。
->タスク定義ファイル（`taskdef.json`）などAWSアカウントIDを指定する必要のあるファイルは、`CodeBuild`の環境変数で置換します。

```:buildspec.yaml
 - echo ########## 【Production】 Prepare Deploy Settings ... on `date`
 - PROD_REPOSITORY_URI=${PROD_ACCOUNT_ID}.dkr.ecr.${AWS_DEFAULT_REGION}.amazonaws.com/${IMAGE_NAME}  #本番のアカウントIDでURIを生成
 - docker tag $REPOSITORY_URI:latest $PROD_REPOSITORY_URI:latest
 - docker tag $REPOSITORY_URI:$IMAGE_TAG $PROD_REPOSITORY_URI:$IMAGE_TAG
 - sed -i -e "s/__ACOUNTID__/$PROD_ACCOUNT_ID/" .prod-config  #クレデンシャルファイルを本番のアカウントIDで置換
 - echo ########## 【Production】 Pushing the Docker image ... on `date`
 - export AWS_CONFIG_FILE=.prod-config #クレデンシャルファイルパスを環境変数から設定
 - $(aws ecr get-login --no-include-email --region $AWS_DEFAULT_REGION --profile cross-account-role) #本番環境で付与したアカウントでECRにログインする
 - docker push $PROD_REPOSITORY_URI:latest #本番アカウントにイメージPush
 - docker push $PROD_REPOSITORY_URI:$IMAGE_TAG
 - echo ########## 【Production】 Writing Task Definitions file...
 - sed -i -e "s/__ACOUNTID__/$PROD_ACCOUNT_ID/" taskdef.json  #タスク定義ファイルを本番のアカウントIDで置換
 - echo ########## 【Production】 Writing image definitions file...
 - printf '{"Version":"1.0","ImageURI":"%s"}' $PROD_REPOSITORY_URI:$IMAGE_TAG > imageDetail.json #イメージ定義ファイルの書き出し
 - echo ########## 【Production】 Sync Deploy Setting file...
 - aws s3 cp appspec.yaml s3://prod-sample-app-auto-deploy/ --acl bucket-owner-full-control #本番アカウントでアクセスできるようにbucket-owner-full-controlオプション付与
 - aws s3 cp taskdef.json s3://prod-sample-app-auto-deploy/ --acl bucket-owner-full-control
 - aws s3 cp imageDetail.json s3://prod-sample-app-auto-deploy/ --acl bucket-owner-full-control
```

```:.prod-config
[profile cross-account-role]
credential_source = EcsContainer
role_arn = arn:aws:iam::__ACOUNTID__:role/auto-deploy-for-fargate ## 1.で作成したロール ※アカウントIDを置換
```

```:taskdef.json
{
  "executionRoleArn": "arn:aws:iam::__ACOUNTID__:role/sample-app-ECSTaskExecutionRole", ## アカウントIDを置換
  "containerDefinitions": [
    {
      "logConfiguration": {
        "logDriver": "awslogs",
        "options": {
          "awslogs-group": "/var/log/sample-app",
          "awslogs-region": "ap-northeast-1",
          "awslogs-stream-prefix": "front"
        }
      },
      "portMappings": [
        {
          "hostPort": 80,
          "protocol": "tcp",
          "containerPort": 80
        }
      ],
      "image": "<IMAGE_NAME>",
      "name": "front"
    }
  ],
  "memory": "512",
  "compatibilities": ["EC2", "FARGATE"],
  "family": "sample-app-front",
  "requiresCompatibilities": ["FARGATE"],
  "networkMode": "awsvpc",
  "cpu": "256"
}
```

```:imageDetail.json
{"Version":"1.0","ImageURI":"__ACOUNTID__.dkr.ecr.ap-northeast-1.amazonaws.com/sample-app:ec5b627"}
```

※設定ファイルの全量は以下を参照してください。
https://github.com/Thirosue/cross-accout-deploy-sample/blob/master/buildspecForCrossAccount.yml

※本番のAWSアカウントへ`AssumeRole`せずにイメージをPushすると`CodePipeline`がトリガーされません（以下、引用元）。
[CodeBuildのビルド内でAssumeRole(クロスアカウントアクセス)する方法とハマった話](https://dev.classmethod.jp/cloud/aws/assumerole-in-codebuild/ "CodeBuildのビルド内でAssumeRole(クロスアカウントアクセス)する方法とハマった話")

## 3. 本番環境（デプロイ先環境）のビルドパイプライン生成

最後に本番環境のビルドパイプラインを`CloudFormation`とマネジメントコンソールで作成します。
-> ターゲットソースは`ECR`
->`S3`に連携したデプロイ設定ファイル（`appspec.yaml`、`taskdef.json`、`imageDetail.json`）を元に`CodeDeploy`で`Fargate`にデプロイします。

### 3-1. パイプラインのビルドステージまで作成

以下`yaml`を`AWS CLI`かマネジメントコンソールで実行します(`CloudFormation`)。

```:yaml
  BuildProject:
    Type: AWS::CodeBuild::Project
    Properties:
      Artifacts:
        Type: CODEPIPELINE
      Source:
        Type: CODEPIPELINE
        BuildSpec: |
          version: 0.2
          phases:
            install:
              runtime-versions:
                docker: 18
            post_build:
              commands:
                - aws s3 cp s3://prod-sample-app-auto-deploy/appspec.yaml . ## 連携されたデプロイ設定ファイルを取得
                - aws s3 cp s3://prod-sample-app-auto-deploy/taskdef.json .
                - aws s3 cp s3://prod-sample-app-auto-deploy/imageDetail.json .
                - cat imageDetail.json
          artifacts:
            files:
                - appspec.yaml
                - taskdef.json
                - imageDetail.json
      Environment:
        ComputeType: BUILD_GENERAL1_SMALL
        Image: aws/codebuild/standard:3.0
        Type: LINUX_CONTAINER
        PrivilegedMode: true
      LogsConfig:
        CloudWatchLogs:
          Status: ENABLED
      Name: !Sub ${ServiceName}-codebuild
      ServiceRole: !Ref CodeBuildServiceRole

  BuildPipeline:
    Type: AWS::CodePipeline::Pipeline
    Properties:
      Name: !Sub ${ServiceName}-codepipeline
      RoleArn: !GetAtt CodePipelineServiceRole.Arn
      ArtifactStore:
        Type: S3
        Location: !Ref ECSDeploySettingBucket
      Stages:
      - Name: Source
        Actions:
        - ActionTypeId:
            Category: Source
            Owner: AWS
            Provider: ECR ## ECR Push Trigger
            Version: 1
          Name: Source
          Configuration: 
            RepositoryName: !Ref ServiceName
          OutputArtifacts:
            - Name: BuildSource
          RunOrder: 1
      - Name: Build
        Actions:
        - ActionTypeId:
            Category: Build
            Owner: AWS
            Provider: CodeBuild
            Version: 1
          Name: Build
          Configuration:
            ProjectName: !Ref BuildProject
          InputArtifacts:
            - Name: BuildSource
          OutputArtifacts:
            - Name: BuildOutPut
          RunOrder: 1
```

※設定ファイルの全量は以下を参照してください。
https://github.com/Thirosue/cross-accout-deploy-sample/blob/master/provisioning/prod/CrossAccountDeployForFargate.yaml

### 3-2. パイプラインのデプロイステージを追加

作成したビルドパイプラインの末尾にBlue/Greenデプロイ設定を追加します。
設定方法は、以下を参照してください。
[ECS FargateでBlue/Greenデプロイ設定](https://qiita.com/takeshi_hirosue/items/0ccacbe8015b3830e1a7 "ECS FargateでBlue/Greenデプロイ設定")

### クロスアカウントの自動デプロイ確認

変更を`GitHub`にPushします。
->サンプルプロジェクトであれば、[index.html](https://github.com/Thirosue/cross-accout-deploy-sample/blob/master/public/index.html "index.html")のタイトルを修正してみてください。

以下のとおり開発環境および本番環境に正常にデプロイされるはずです。

<img width="1368" alt="スクリーンショット 2020-01-12 21.38.18.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/73176/75b69fd5-41bc-4d4b-d2d8-5088440cc053.png">