AnsibleをAWS Systems Managerから実行するCI/CDを構築する。（その５：CodePipelineを使用したCI/CD）

# はじめに
前回は`CodeBuild`を利用して`Systems Manager`で`Ansible`を実行してみましたが、今回は今まで作成してきたサービスを繋ぎ合わせて、`CodeCommit`へのプッシュを契機に`CodeBuild`を実行、`Slack`へ通知を行ってみようと思います。

- 【前】[AnsibleをAWS Systems Managerから実行するCI/CDを構築する。（その４：CodeBuildからのAnsible実行）](https://qiita.com/sakai00kou/items/5c100bc6b46864332f1d)

# CodePipelineとは
`CI`（継続的インテグレーション）、`CD`（継続的デリバリ）を実現するために、特定の操作を契機とした連携処理を設定するためのサービスです。

例えば、`CodeCommit`へのプッシュを契機に、ソースコードのビルドやビルド後のデプロイ等の作業を一気通貫で自動で行うように繋ぎ合わせるサービスが`CodePipeline`となります。

# 今回の構成
[その１](https://qiita.com/sakai00kou/items/5604b94dd2c87922538f)で掲載した構成図から少し書き換えていますが`CodePipeline`で繋ぎ合わせて以下のような構成を作ってみようと思います。

```plantuml
@startuml
!define AWSPuml https://raw.githubusercontent.com/awslabs/aws-icons-for-plantuml/v13.1/dist
!include AWSPuml/AWSCommon.puml
!include AWSPuml/Groups/AWSCloud.puml
!include AWSPuml/General/Client.puml
!include AWSPuml/DeveloperTools/CodeCommit.puml
!include AWSPuml/DeveloperTools/CodeBuild.puml
!include AWSPuml/DeveloperTools/CodePipeline.puml
!include AWSPuml/ManagementGovernance/SystemsManagerRunCommand.puml
!include AWSPuml/ApplicationIntegration/SimpleNotificationService.puml
!include AWSPuml/ManagementGovernance/Chatbot.puml
!include <logos/slack.puml>
!include AWSPuml/AWSSimplified.puml

!procedure $stepnum($number) 
<back:black><color:white><b> $number </b></color></back>
!endprocedure

AWSGroupColoring(CodePipelineGroup, #FFFFFF, AWS_COLOR_BLUE, dashed)
!define CodePipelineGroup(g_alias, g_label="CodePipeline") AWSGroupEntity(g_alias, g_label, AWS_COLOR_BLACK, CodePipeline, CodePipelineGroup)

skinparam shadowing false
hide stereotype
'skinparam linetype ortho
skinparam rectangle {
    BackgroundColor AWS_BG_COLOR
    BorderColor transparent
}

left to right direction
'top to bottom direction
title CI/CD構成
rectangle "$ClientIMG()\nClient" as cl
AWSCloudGroup(cloud){
  CodePipelineGroup(pipeline) {
    rectangle "$CodeCommitIMG()\nCodeCommit" as commit
    rectangle "$CodeBuildIMG()\nCodeBuild" as build
    rectangle "$SystemsManagerRunCommandIMG()\nRunCommand" as ansible
  }
  rectangle "$SimpleNotificationServiceIMG()\nSNS" as sns
  rectangle "$ChatbotIMG()\nChatBot" as chat
}
rectangle "<$slack>" as slack

cl-->commit :$stepnum("1")Playbookをpush
commit-->build :$stepnum("2")push検知
build-->ansible :$stepnum("3")CodeBuildで実行(dry-run)
cl-->pipeline :$stepnum("4")承認通知
build-->ansible :$stepnum("5")CodeBuildで実行(apply)
ansible-->sns :通知
sns-->chat
chat-->slack :ChatBotからSlackへ通知
@enduml
```

図だけだと分かりづらいので、手順の流れを書くと以下のようになります。

1. クライアント端末から`CodeCommit`へプッシュ
2. `CodeCommit`へのプッシュを契機に`CodePipeline`が`CodeBuild`を実行
3. `CodeBuild`から`Systems Manager`（`Ansible`）を実行（チェックのみ）
4. クライアント端末から`CodePipeline`のページに行き、承認処理を実行
5. `CodeBuild`から`Systems Manager`（`Ansible`）を実行（反映）

`Slack`への通知については上記のそれぞれのタイミングで通知されるようになっています。

# CodePipelineの作成
`CodePipeline`のダッシュボードから「**パイプラインを作成する**」を選択して`CodePipeline`を作成していきます。

パイプライン名は任意ですが、今回は`ssm_ansible_pipeline`とし、サービスロールは新しく作成しておきます。

![Monosnap_20221204_141509.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/569912/43d486ae-f458-f1db-c049-c34860b526a5.png)

前述の通り`CodeCommit`へのプッシュを契機とするため、ソースは`CodeCommit`、リポジトリは「[その2](https://qiita.com/sakai00kou/items/5977f76cb3c8a182e840)」で作成した`ssm_ansible`、ブランチは`master`、その他はデフォルトのまま次へ進みます。

![Monosnap_20221204_141820.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/569912/9d462ff8-3a1e-40b3-5e0a-4027c1a35995.png)

ビルドステージでは「[その4](https://qiita.com/sakai00kou/items/5c100bc6b46864332f1d)」で作成した`ssm_ansible`、その他はデフォルトのまま次へ進みます。

![Monosnap_20221204_141929.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/569912/6f7caf9a-56f6-fcde-5fe5-7641a32742a1.png)

デプロイは今回`CodeBuild`経由で実行する`Systems Manager`で実行するため、デプロイステージはスキップします。

![Monosnap_20221204_142059.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/569912/597307be-e373-24e6-ceaf-242550a54a7e.png)

「**パイプラインを作成する**」で作成すると、一旦現在までの状態で一連の`Pipeline`が実行されるため、処理を見届けてエラーとならなければOKです。

![Monosnap_20221204_142225.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/569912/ce61c20d-04a1-adfe-2606-9187c8422535.png)

![Monosnap_20221204_144725.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/569912/6a480023-3a9e-e362-71b3-a185c8befa12.png)

## 通知ルールの作成
ここまでで`CodeCommit`→`CodeBuild`の実行はできましたが、「**通知ルール**」を作成して、特定のトリガーで`Slack`へ通知するようにします。

`CodePipeline`の成功画面の上部に「**このpipelineの通知ルールを作成する**」のボタンがあるので、そちらから通知ルールを作成していきます。

![Monosnap_20221204_145321.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/569912/d19d3ddd-1f7b-dfd8-3f0d-ac5efba5d6f9.png)

トリガーするイベントは、あまりチェックを付けすぎてもうるさいだけなので、`CodePipeline`が`Succeeded`か`Failed`だけチェックを付けておきます。

「**ターゲット**」は、「**AWS Chatbot (Slack)**」を選び、「[その3](https://qiita.com/sakai00kou/items/b75e763aef0af475dfb2)」で作成した`Chatbot`のチャンネル名を選択して「**Submit**」で作成します。

![Monosnap_20221204_194956.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/569912/91ccb34e-7872-4385-3af3-3cdeecbbff03.png)

ここでチャンネル名を選んで「**Submit**」をすることで裏では`CodePipeline`からの通知用のSNSトピックの作成と`Chatbot`へのSNSトピックの結びつけが行われています。

「**CodeStarNotifications-[Chatbotチャンネル名]-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx**」と言った名前のSNSトピックが自動で作成されるため、見知らぬSNSトピックだからといって削除しないようにしてください。（私は削除してハマりました・・・）

## 承認処理の追加
`CodeBuild`による`Systems Manager`（`Ansible`）の実行後、一旦人のチェックを行うため承認処理を追加しようと思います。

先程作成したパイプラインに処理を追加するためには、`CodePipeline`のダッシュボードより、「**パイプライン・CodePipeline**」→「**パイプライン**」→「**先程作成したパイプライン名**」を選択し、「**編集**」から処理を追加できます。

既存のステージを編集して、ステージ内に処理を追加することもできますが、今回は新たにステージを追加して承認処理を追加します。

![Monosnap_20221204_150617.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/569912/b374e962-735a-35b8-5837-fae9d4fbbc46.png)

承認処理なので名前は「**Approval**」にしておきます。（日本語不可）

![Monosnap_20221204_150947.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/569912/c25088f5-a4a2-7bab-e8fd-683f469266cc.png)

ステージを追加すると、作成した「**Approval**」が追加されるため、「**アクショングループを追加する**」を選択し、以下のように設定します。

SNS通知先は別の通知先にすることもできますが、今回はテストなので先程のトリガー通知先と同じ`Slack`チャンネルにしました。

![Monosnap_20221204_151241.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/569912/ceaabbed-49b0-b976-6d71-718952d3bdce.png)

## 反映処理の追加
最初に実施した`CodeBuild`では、`Ansible`を`Dry-Run`（チェックモード）で動かしていたため、対象インスタンスへの反映は行われていませんが、承認処理を行った後、実際に対象インスタンスへ設定を反映するようにします。

具体的には「[その4](https://qiita.com/sakai00kou/items/5c100bc6b46864332f1d)」で変数設定により`CHECK: True`をしていましたが、この変数を上書きして`CHECK: False`とすることで反映処理を実行するようにします。

先ほどと同様、「**ステージを追加する**」より、今回は「**Apply**」というステージ名を付け、以下のような`CodeBuild`のアクションを作成します。

![Monosnap_20221204_152322.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/569912/412accbc-42bf-7e5d-0875-2dc74e9594b4.png)

![Monosnap_20221204_152141.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/569912/87988998-dd14-c258-cf83-a464705715dd.png)

忘れてはいけないのが「**環境変数 - オプショナル**」より`CHECK`変数を指定して`False`としておくことが必要です。

`buildspec.yml`の中でも環境変数として　`CHECK`の設定を行っていますが、ここで指定する環境変数は`CodePipeline`実行時に`buildspec.yml`の中の環境変数を上書きするため、ここで　`CHECK`を`False`とすることで、`Apply`処理で`Ansible`が動作する仕組みです。

すべて完成すると、最初に作成していた「**Source**」、「**Build**」ステージの下に以下「**Approval**」、「**Apply**」が追加されていることが確認できるかと思います。

![Monosnap_20221204_152422.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/569912/81af7202-06aa-6e13-216f-50d320f87aa5.png)

# 一連の動作確認
ここまでで一連の処理は設定できたので、実際に`CodeCommit`へのプッシュ契機で動作するか確認してみます。

ただ、何も変更していない状態でのプッシュだと反応しないため、処理に影響が出ない変更を行った上でプッシュします。


うまく`CodePipeline`が設定できていれば以下のように承認処理まで進むはずです。

承認後、後続の処理が動きはじめ、エラーなく終了すれば完了です。

今回、`CodePipeline`の`Succeeded`と`Failed`のときに通知するよう設定していたため、「[その3](https://qiita.com/sakai00kou/items/b75e763aef0af475dfb2)」で設定した`Slack`のチャンネルには以下のように通知が飛んでいるかと思います。（赤枠が今回通知されたメッセージ）

![Monosnap_20221204_200231.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/569912/6f92fdf8-3242-d6df-eb93-ea0bc163a06e.png)


# おわりに
5回に渡り`CodePipeline`を使用した`CI/CD`の構成を組んでみましたが、１回のイベント契機で某ピタゴラスイッチのように続けて処理がされるところを見るのは楽しいですね。

処理を繋げていくこと自体は今に始まったことではなく、昔から行われてきたことですが、今のように画面上からサービスを繋ぎ合わせたり、簡単な命令文を書くだけで様々な処理を繋げられるという点が、`CI/CD`と言った言葉が生まれて流行っている理由なんだろうなと感じました。