【AWS】CodePipelineを利用したCloudFormationテンプレートのデプロイ【Pipeline】

# はじめに
デプロイを行う際に改修したプログラムなどのリソースの配置と内容の確認まで行い、デプロイ担当者がリリース用のボタンを押すだけの状態で待ちたい場合があります。
今回の記事ではCloudFormationテンプレートの作成者が変更セットを作成し、デプロイ担当者が内容を確認してデプロイを実施する場合を想定して実現例を記載しました。

# 今回の想定ケース
・開発担当者とデプロイ担当者が別の人が担当している
・デプロイする前日には開発担当者がデプロイ対象のリソースを配置し、デプロイ担当者が内容を確認する(内容を確認して誤りがあればデプロイを中止する)
・デプロイ当日はデプロイ担当者がデプロイを承認するだけの作業にする

# 今回の記事のポイント
・CodePipelineで一連のデプロイ手順を管理する
　※CodePipelineの定義内容はAWS CDKで定義する
・デプロイのソースはS3に配置する。
・S3に配置されたらリソースを元にCodePipelineはCloudFormationの変更セットを作成する
・変更セットが作成されたらAWS ChatBot経由でデプロイ担当者(Slack)に通知を送る
　※この時、CodePipelineはデプロイ担当者の承認を得るまでデプロイを中断する
・デプロイ担当者が承認を行った場合に変更セットの実行(デプロイ)を行う

# こんな場合に有効
・CloudFormationをマネジメントコンソールから操作してデプロイ担当者へ確認依頼などデプロイ作業しているが、もう少し手順を自動化して簡略化したい
・デプロイ対象のリソースとデプロイ手順を一括でIaC管理するのではなく、デプロイ対象とデプロイ手順を分けてIaC化したい
・デプロイの実行履歴を一元的に管理したい

## 構成図
・開発担当者がテンプレートを作成してテンプレートをアップロードしデプロイ担当者が内容を確認して承認する運用の様子とデプロイに関する各サービスの連携がCDK管理されている様子を図にしました

![pipeline.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/192949/e0a20028-c64d-7b34-64c2-57d73c78c859.png)

# この記事のキーワード
## 変更セット(Change Set)
CloudFormationでリソースをデプロイする際に、前回のデプロイと比較して今回のCloudFormationテンプレートにより追加、更新、削除されるリソースを確認できる機能
CloudFormationテンプレートを実行により意図しないリソースがデプロイされてしまう問題を、事前に確認することにより防ぐことができる


# 実装内容（CDKの定義内容）
## import
・使用するAWSリソースをインポートに含めます
```javascript:cdk-stack.ts
import * as cdk from 'aws-cdk-lib';
import { Construct } from 'constructs';
import { Pipeline, Artifact } from 'aws-cdk-lib/aws-codepipeline';
import { S3SourceAction, CloudFormationCreateReplaceChangeSetAction, CloudFormationExecuteChangeSetAction, ManualApprovalAction } from 'aws-cdk-lib/aws-codepipeline-actions';
import { Bucket } from 'aws-cdk-lib/aws-s3';
import { CfnNotificationRule } from 'aws-cdk-lib/aws-codestarnotifications'
import { SlackChannelConfiguration } from 'aws-cdk-lib/aws-chatbot';
```

## S3バケット定義

・CloudFormationテンプレートを配置するS3バケットを定義しています

```javascript:cdk-stack.ts
    // S3バケットの作成
    const 【S3定義名】 = new Bucket(this, '【S3定義名】', {
      bucketName: '【バケット名】',
      versioned: true,
    });
```

##  CodePipelineの定義
<ソースステージ定義>
・ここではS3バケットをソースとして設定していますが、GitHubなどを設定することも可能です

:::note warn
bucketKeyのファイル名の拡張子は「zip」
CloudFormationファイルはjsonやyamlの形式で作成されますがjsonやyamlファイルのまま設定してしまうと、この工程のoutputでランダムな文字列のファイル名になるため、後の工程でアップロードしたファイル名で処理ができません
S3にアップロードしてSourceステージで処理するファイルは「zip」形式にしてください
:::

```javascript:cdk-stack.ts
    // CodePipelineの定義
    const sourceOutput = new Artifact('SourceArtifact');
    const changeSetOutput = new Artifact();

    const 【パイプライン定義名】 = new Pipeline(this, '【パイプライン定義名】', {
      pipelineName: 'CodePipelineTest2502',
      stages: [
        {
          stageName: 'Source',
          actions: [
            new S3SourceAction({
              actionName: 'S3Source',
              bucket: 【S3定義名】,
              bucketKey: '【アップロードファイル名】.zip', // テンプレートのパスを指定
              output: sourceOutput,
            }),
          ],
        },
```

＜CloudFormation変更セット作成ステージ＞
・変更セットのもとになるスタックのスタック名と変更セット名を設定します
・テンプレートファイル名は前のソースステージで設定したzipファイルではなく、zipファイルに入っているCloudFormationのテンプレート名を設定してください
```javascript:cdk-stack.ts
        
        {
          stageName: 'CreateChangeSet',
          actions: [
            new CloudFormationCreateReplaceChangeSetAction({
              actionName: 'CreateChangeSet',
              stackName: '【スタック名】',
              changeSetName: '【変更セット名】',
              adminPermissions: true,
              templatePath: sourceOutput.atPath("【テンプレートファイル名】.yaml"), 
              output: changeSetOutput,
            }),
          ],
        },
        
```

＜手動承認ステージ＞
・手動承認ステージを設定することでデプロイを待機します
・変更セットを確認したデプロイ担当者がコンソール画面で「承認」か「拒否」を行います
・承認の場合は次のステージに進み、拒否の場合はこのパイプラインを失敗扱いで終了します
```javascript:cdk-stack.ts
        {
          stageName: 'Approval',
          actions: [
            new ManualApprovalAction({
              actionName: 'ManualApproval',
            }),
          ],
        },

```

＜変更セット実行ステージ＞
・変更セットを実行し、デプロイを実行します
・CloudFormation変更セット作成ステージで設定したスタック名と変更セット名を設定します
```javascript:cdk-stack.ts
        {
          stageName: 'ExecuteChangeSet',
          actions: [
            new CloudFormationExecuteChangeSetAction({
              actionName: 'ExecuteChangeSet',
              stackName: '【スタック名】',
              changeSetName: '【変更セット名】',
            }),
          ],
        },
      ],
    });
```


##  ChatBot通知設定

・CodePipelineの通知ではChatBotを利用して通知ができるため、ChatBotの定義を行います
・ChatBotはメッセージを受け取るワークスペースIDとチャンネルIDを設定してください

:::note warn
チャンネルIDはチャンネル名にあらず
チャンネルIDはスラックのURLから取得可能です
https：//app.slack.com/client/【ワークスペースID】/【チャンネルID】
↑SlackのURLはこのようになっているはずですので該当する箇所からワークスペースIDとチャンネルIDを取得してください
:::

```javascript:cdk-stack.ts
    // Slack通知の設定
    const 【ChatBot定義名】 = new SlackChannelConfiguration(this, '【ChatBot定義名】', {
      slackChannelConfigurationName: '【設定名D】',
      slackWorkspaceId: '【SlackワークスペースID】',
      slackChannelId: '【チェンネルID】',
    });
```


##  CodePipelineの通知設定
・CodePipelineの通知設定を定義します
・ChatBotへの通知依頼はAmazonSNSではなく、CodePipelineの通知設定から行うことが可能です
・今回の例では'codepipeline-pipeline-manual-approval-needed'を指定することで手動承認が要求された段階で通知がされるように設定しています
・ChatBot定義名からChatBotの「ARN」を取得するように設定

```javascript:cdk-stack.ts
    // 手動承認ステップのみを対象とする通知ルールの設定
    new 【通知定義名】(this, '【通知定義名】', {
      name: '【通知名】',
      detailType: 'FULL',
      eventTypeIds: [
        'codepipeline-pipeline-manual-approval-needed' // 手動承認時に通知
      ],
      resource: 【Pipeline定義名】.pipelineArn,
      targets: [
        {
          targetType: 'AWSChatbotSlack',
          targetAddress: 【ChatBot定義名】.slackChannelConfigurationArn
        }
      ]
    });
    
```


:::note warn
手動承認以外の通知設定
手動承認が求められた以外にもキャプチャ画像に示したように
手動承認の失敗、要求、成功
パイプラインの開始、成功、失敗、中断
各ステージの開始、成功、失敗、中断
など、様々な通知が可能です

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/192949/5e813459-f7b9-9b62-5794-2d854b5440d0.png)

以下にCDKで通知をトリガーする設定をする際の例を示します。
CDKで定義したい内容に応じて設定を行ってください。
※最後のfailedやsucceededのところをキャプチャで示した内容に変えれば各カテゴリの各パターンに応じた通知ができます
|設定値  |説明  |
|---|---|
|codepipeline-pipeline-action-execution-failed  |アクションの実行が失敗  |
|codepipeline-pipeline-pipeline-execution-suceeded  |パイプラインの実行がせいこう  |
|codepipeline-pipeline-stage-execution-succeeded	  |ステージの実行が成功  |
|codepipeline-pipeline-manual-approval-needed  |手動承認が必要  |

:::

# 動作検証

## CloudFormationテンプレートのS3アップロード
・CloudFormationのテンプレートを含むzipファイルをCodePipeline用のS3バケットにアップロードします
↓S3へのファイルアップロードを確認する画面キャプチャ
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/192949/7183915b-ebc0-823f-dc94-2851d1d168aa.png)

## CodePipeline実行確認
・CodePipelineの設定でS3バケットの変更検知する設定している場合はS3バケットにファイルをアップロードすると間もなくPipelineが開始されます
↓S3へのファイルアップロードを検知して変更セット作成ステージまで進んだことを確認した画面キャプチャ
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/192949/bde0f712-0678-2b5a-5e2f-b34287ce2176.png)


## 変更セットの作成
・変更セット作成フェーズで変更セットが作成されると、手動承認ステージに進みます。手動承認ステージは承認か拒否が行われるまで「保留中」のステータスになります

↓CodePipelineの手動承認まで進んだことを示す画面キャプチャ
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/192949/4d2a1055-46dc-54c4-90e3-e3c097b09d21.png)

・CloudFormationのコンソール画面で作成された変更セットが確認できます
・想定する運用ではこの変更セットをデプロイ担当者が確認して、予定にないデプロイがされないかを確認します
↓CloudFormationの変更セット
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/192949/7abc488d-8699-b1e8-32f6-ffcb8e275171.png)


## 通知の確認
・ChatBotから通知が行われSlackに通知が受信されました
・通知からパイプライン名とステージ名、手動承認を要求していることがわかります
↓Slack通知の画面キャプチャ
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/192949/cc316733-0d71-2b02-1db9-81e41990227b.png)

## 手動承認ステージ
・通知を受け取ったデプロイ担当者が変更セットを確認し、承認か拒否を行います
・承認か拒否は「レビュー」ボタンからレビューダイアログを開き、どちらかを選択します
・コメントを入力して承認か拒否を選択することもできます
↓レビュー画面
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/192949/9d764891-e815-2936-2ecc-f21856d2c142.png)

## デプロイ内容の確認
・手動承認ステージで「承認」が選択されると後続処理に設定された変更セットの実行が行われます
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/192949/55334d40-8fe7-fb80-d5e9-edf7c2a837b8.png)

## 過去のデプロイ履歴の確認
・CodePipelineの履歴画面から過去の成功か失敗した履歴が確認できます
↓履歴画面の実行履歴一覧の画面キャプチャ
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/192949/d85ead8b-bb90-2842-3ae0-e53f866d8378.png)
・実行履歴の詳細では手動承認時に設定したコメントも確認できます
↓手動承認時のコメント確認の画面キャプチャ
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/192949/fa434cc2-822c-1c00-ae97-33d3d6776022.png)

# さいごに
前回の記事ではデプロイするリソースとCodeDeployを同じCDKで定義してリリースしましたが
今回はリソースとデプロイ手順のパイプラインを別のIaC管理としました
デプロイ手順のパイプラインが別のIaCとして管理できれば別のプロジェクトにも流用がしやすいと思うので
リソースとパイプラインのより良い管理形態をこれからも勉強していきたいです


私の前回の記事

https://qiita.com/Nana_777/items/4627fc27713217ca93db

CDKの初期設定手順についての私の記事

https://qiita.com/Nana_777/items/e86fae02989c98dc986d