AnsibleをAWS Systems Managerから実行するCI/CDを構築する。（その１：Systems ManagerからのAnsible実行）

# はじめに
最近勉強がてら「**[AWS Hands-on for Beginners](https://aws.amazon.com/jp/aws-jp-introduction/aws-jp-webinar-hands-on/)**」の「**[AWS Code サービス群を活用して、CI/CD のための構成を構築しよう！](https://pages.awscloud.com/JAPAN-event-OE-Hands-on-for-Beginners-cicd-2022-reg-event.html?trk=aws_introduction_page)**」を視聴し、自分でも`CI/CD`の環境を構築してみたいと思ったので、[前回](https://qiita.com/sakai00kou/items/5f8e9ecbc1e38f958eb2)手放しでおすすめできるとは言い難いと記事に書いておきながらも、`Systems Manager`から`Ansible`実行機能を使って、`CodeCommit`に`Ansible`実行用のソースをコミットしたら指定のインスタンスに対して`Ansible`を実行する環境を構築してみたいと思います。

- 【関連1】[Amazon S3からのスクリプトの実行をやってみた。](https://qiita.com/sakai00kou/items/326a36fbdea10a6b2da8)
- 【関連2】[SSM Agentの実行スクリプトを削除する方法](https://qiita.com/sakai00kou/items/f6e04760783b805d55d5)
- 【関連3】[Systems Managerでベストプラクティス構成のPlaybook運用する場合に注意すること](https://qiita.com/sakai00kou/items/5f8e9ecbc1e38f958eb2)

---
- 【次】[AnsibleをAWS Systems Managerから実行するCI/CDを構築する。（その２：CodeCommitの準備）](https://qiita.com/sakai00kou/items/5977f76cb3c8a182e840)

# 最終的な構成
今回から数回に分け、以下のような構成を構築してみようと思います。

```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/Storage/SimpleStorageServiceBucket.puml
!include AWSPuml/ManagementGovernance/Chatbot.puml
!include <logos/slack.puml>
!include AWSPuml/AWSSimplified.puml

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 "$SimpleStorageServiceBucketIMG()\nS3" as s3
  rectangle "$SimpleNotificationServiceIMG()\nSNS" as sns
  rectangle "$ChatbotIMG()\nChatBot" as chat
}
rectangle "<$slack>" as slack

cl-->commit :Playbookをpush
commit-->build :push検知
build-->s3 :Artifact Upload
build-->ansible :CodeBuildで実行
ansible-->sns :通知
sns-->chat
chat-->slack :ChatBotからSlackへ通知
ansible<--s3 :Artifact Download
@enduml

```

- [拡大図](//www.plantuml.com/plantuml/png/bLFDQXn74BxtKxGLWxMmgy7ejYZ2sf4ZHFmZl36v14JFJErCevwkeRj6GWH3v5E2SmWvXe099FYMSzvdYPAyHRfxMkEL9WFH7dRgfxwllgxkUc9966fhnHuKi98Q-C6NonFdyugeiVCaCU9YfvHKjLbhmUIe2JJjv5Wdui8gaLd_dKcNiBCLcbcZXFQ4oVdZtPt7II4jiGTIvwejUlB4VLEiQzGxZNE7-IE3RMD3cSAs6Ai23KQe95KID8qK7S8vA6p0V86eR99Y0Rwrl4VzefMg-F_b9x819JMC89uBBKgeGTCHdeFHGkUGB2yjGMsxd7dLQgzHwB6k1qsZP2v8eZxM1AK9PhAKTQFW1P9SnUmIpBdCnwGi2Oth5t7HYv15cvy1tUSCQIKemxkGFOKbsiGgaP-5rFv_FeSWmSc6U6JcOk4LfAZGI5rkRuusPARyay_2R-g9JjENprw-EbqyU_rqoWjXAoWc_QCUOBVBKw6ai5DUdYgHWVfqQxDcQy9l-Z_L9EboK7-xvq7w-VHMa-cmvOGnUoPr8uoekQr4WHVkM7mbb0LMoGAu9J20TDa0U_Inr1Fu64T35Mvm6CX9w581_vfnzrku2PUkaox2p8B0nL6diIj0Ku3facI4jeu7DB4tZ2bO4IVaHfOLyK9wRecQFI9iV3n38gmt4YJ9DKwFa_Jm-iExwsz_O1_bR3tiDlBu-T7sv2lTELjSM9uhThFJtJNatfnu_SChQg8tYUVRRD2lST-a3tIDWZc0zTlSew8VG9ct1fYnrOmKVQ3Z4DhAJ85ZUSFky8mkMMHQxWO6kpi0Zcpr3Ux5iWDgUmSPDpBMUM-1SJxETlU-KRptCApeVaW7ax5SpMRxtIZv_4I9ompnRFtsnwQr5UlYhY2CZSzzyFhNd_zw_niB4PUnktn-O9nkaHD_tIWKHP-BW-Bp_WxMLn_-_FtztxzypsBECsZBv_zyyvDdTRQB1EN-pzb19f_7WwsltgtVVhVqiVNL7n4KgVPcirjY3l521pbFG1TjhVu5)



# Systems ManagerからのAnsible実行について
`Systems Manager`の`RunCommand`実行機能の一つで、対象となるノードに対して`ansible-playbook`を実行できる機能となります。

１ファイル構成の`playbook`実行と、いわゆるベストプラクティス構成となる`playbook`の実行それぞれに対応しているため、すでに`Ansible`を使って運用している環境であれば、比較的スムーズに移行することが可能です。

- [Ansibleベストプラクティス](https://docs.ansible.com/ansible/2.9_ja/user_guide/playbooks_best_practices.html)

`Systems Manager`で`RunCommand`機能を使った場合の動作や仕組みなどについては上記[【関連1】](https://qiita.com/sakai00kou/items/326a36fbdea10a6b2da8)で説明しているため、そちらを参照してください。

`Systems Manager`で`RunCommand`機能を使った`Ansible`実行で注意することは上記[【関連3】](https://qiita.com/sakai00kou/items/5f8e9ecbc1e38f958eb2)で説明しているため、そちらを参照してください。

# Systems ManagerからのAnsible実行手順
実際に`Systems Manager`から`Playbook`を実行するには以下のような手順で進めていきます。

流れとしては「[Amazon S3からのスクリプトの実行をやってみた。](https://qiita.com/sakai00kou/items/326a36fbdea10a6b2da8)」と同様の`RunCommand`機能を使用して実行しているため、ほぼ同等の流れとなります。

- SSM Agentインストール
- S3バケットへのPlaybook格納
- S3&Systems Managerアクセス用ロールの作成
- 対象のEC2インスタンスにS3&Systems Managerアクセス用ロールをアタッチ
- Amazon S3からのスクリプトの実行

以下以前の記事と被る部分は省略しつつ説明していきます。

## SSM Agentインストール
「[Amazon S3からのスクリプトの実行をやってみた。](https://qiita.com/sakai00kou/items/326a36fbdea10a6b2da8)」と同等の手順となるため、割愛。

## S3バケットへのPlaybook格納
今回はベストプラクティス構成の`Playbook`実行を試してみようと思うので、以下のようなファイル・ディレクトリ構成の`Playbook`をS3に格納していきたいと思います。

```bash:ファイル・ディレクトリ構成
ansible_playbook
├── group_vars
│   └── all.yml
├── roles
│   ├── packages
│   │   └── tasks
│   │       └── main.yml
│   └── users
│       └── tasks
│           └── main.yml
├── site.yml
└── test_server.yml
```

```yml:group_vars/all.yml
# パッケージインストール
yum_install:
  - { name: "gcc" }

# test-groupグループ作成
add_group:
  - { name: "test-group", gid: "1100" }

# test-userユーザ作成
add_user:
  - { name: "test-user", group: "test-group", uid: "1100" }
```

```yml:roles/packages/tasks/main.yml
---
# tasks file for packages
- name: Package Install
  ansible.builtin.yum:
    name: "{{ item.name }}"
    state: present
  with_items: "{{ yum_install }}"
```

```yml:roles/users/tasks/main.yml
---
# tasks file for users
- name: add group
  ansible.builtin.group:
    name: "{{ item.name }}"
    state: present
    gid: "{{ item.gid }}"
  with_items: "{{ add_group }}"

- name: add user
  ansible.builtin.user:
    name: "{{ item.name }}"
    group: "{{ item.group }}"
    uid: "{{ item.uid }}"
    state: present
  with_items: "{{ add_user }}"
```

```yml:site.yml
---
- import_playbook: test_server.yml
```

```test_server.yml
- name: test_server playbook
  hosts: all
  become: yes
  roles:
    - { role: packages }
    - { role: users }
```

尚、S3への格納方法は無圧縮と`Zip`形式に対応しているため、無圧縮でベストプラクティス構成の`Playbook`をS3に格納してもよいですが、今回は`Zip`形式に圧縮してからS3に格納しようと思います。

以下`Mac`で`Zip`ファイルを作成した場合の例。

```bash:MacでZip圧縮
zip -r ansible_playbook.zip ansible_playbook
```

圧縮したソースを適当なバケット直下に格納しておきます。

## S3&Systems Managerアクセス用ロールの作成
「[Amazon S3からのスクリプトの実行をやってみた。](https://qiita.com/sakai00kou/items/326a36fbdea10a6b2da8)」と同等の手順となるため、割愛。

## 対象のEC2インスタンスにS3&Systems Managerアクセス用ロールをアタッチ
同上

## Amazon S3からのスクリプトの実行
いきなり最終構成には持っていけないため、今回は以下のような`Systems Manager`の`RunCommand`で`Ansible`を単体で実行してみるところまで実施してみます。

```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/AWSSimplified.puml
!include AWSPuml/General/Client.puml
!include AWSPuml/ManagementGovernance/SystemsManagerRunCommand.puml
!include AWSPuml/Storage/SimpleStorageServiceBucket.puml

skinparam shadowing false
left to right direction
'top to bottom direction
title Systems Manager Run CommandでのAnsible実行

Client(cl,"Client","Client")

AWSCloudGroup(cloud){
  SystemsManagerRunCommand(ansible,"RunCommand","RunCommand")
  SimpleStorageServiceBucket(s3,"S3","S3")
}

cl-d->ansible :Ansibleの実行命令
s3-u->ansible :Playbook Download
@enduml
```

### コマンドドキュメント
`Systems Manager`の`RunCommand`から`Ansible`を実行する方法として以下２つの方法がありますが、`AWS-RunAnsiblePlaybook`は廃止とのことで互換性のため残されているだけのようです。

そのため、新たに使う場合は`AWS-ApplyAnsiblePlaybooks`を選択すれば問題ありません。

また、`AWS-ApplyAnsiblePlaybooks`は、コマンド実行時、デフォルトで`Ansible`の実行に必要となるパッケージやモジュールは自動でインストールするようできているため、`RunCommand`実行のために事前に対象ノードに`Ansible`や`Ansible`インストールに伴う依存パッケージのインストールを行うようなことはしなくて済むようにできています。

|コマンドドキュメント名|説明|
|:--|:--|
|AWS-ApplyAnsiblePlaybooks|複雑なPlaybook実行サポート|
|AWS-RunAnsiblePlaybook|廃止|

- [チュートリアル: Ansible プレイブックを実行する関連付けの作成](https://docs.aws.amazon.com/ja_jp/systems-manager/latest/userguide/systems-manager-state-manager-ansible.html)

### Run Commandの実行
実際にマネジメントコンソールより`RunCommand`で`Ansible`を実行していきます。

「**Systems Manager**」→「**ノード管理**」→「**Run Command**」から「**Run command**」を選択します。

![Monosnap_20220911_152633.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/569912/f98e2a64-926e-ac40-5e54-8b8fc8cd9d92.png)

「**コマンドドキュメント**」は前述の通り`AWS-ApplyAnsiblePlaybooks`を選択。

![Monosnap_20220911_152755.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/569912/3768c586-8931-a5ad-a858-25e5b7e7c56d.png)

同画面下の「**コマンドのパラメータ**」は以下のように設定していきます。

基本的にはソース設定と`Check`設定（いわゆるDryRunで動作させるかの設定）以外はデフォルトのままで設定。

|パラメータ名|パラメータ|説明|
|:--|:--|:--|
|Source Type|S3|ソースファイル格納先|
|Source Info|{"path":"https://[**バケット名**].s3.[**リージョン名**].amazonaws.com"}|ソースダウンロード先S3のURL|
|Install Dependencies|True|依存パッケージ自動インストール有無|
|Playbook File|ansible_playbook/site.yml|Zip解凍時にディレクトリ作成されるため左記パスで指定|
|Extra Variables|SSM=True|デフォルト設定|
|Check|False|AnsibleをCheck Modeで動作させるかの設定<br>今回は反映で設定|
|Verbose|-v|ログ詳細表示設定|
|Timeout Seconds|3600|実行タイムアウト設定|

`Source Info`は`JSON`形式で設定する必要があるので表のように指定します。

`Playbook File`の設定はソースファイルを`S3`へ無圧縮でアップロードした場合でも、ディレクトリ構成は判断してくれるため、アップロード先のパスに合わせて`site.yml`を指定するだけで問題ありません。

### ターゲット
「**インスタンスを手動で選択する**」を選択し、テスト用に作成したEC2インスタンスをチェックします。

![Monosnap_20220911_154820.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/569912/7bbaf644-060d-ba24-70d4-cbb1121634f9.png)

### その他の設定
上記設定以外に以下のようなパラメータ設定項目がありますが、今回は`Ansible`の動作を確認したいだけなので、通知や出力は行わずに実行を行います。

|項目名|設定内容|
|:--|:--|
|その他のパラメータ|デフォルトのまま|
|レート制御|デフォルトのまま|
|出力オプション|S3バケットに書き込まないように設定|
|SNS通知|通知しない|

## 実行結果の確認
実行後、問題なければ以下のように成功します。

![Monosnap_20220911_160243.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/569912/40de3196-bec8-a8f1-3acf-d06857594452.png)

また、**インスタンスID**を選択すると以下のように`Playbook`実行結果やエラー内容を確認することが可能です。

![Monosnap_20220911_160352.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/569912/57b3421f-a56b-12c7-1dbc-0d5c2e39cc05.png)

もし失敗していた場合は、対象ノードに割り当てたIAMロールの権限や`Playbook`のパス、エラーログ等を確認しましょう。

# おわりに
今回はCI/CD構成を構築する前段階として`Systems Manager`の`RunCommand`機能を使って`Ansible　Playbook`を実行してみました。

今回は圧縮したソースファイルを直接`S3`にアップロードしましたが、最終的には`CodeCommit`へのコミットを契機として実行するように構築するため、次回は`CodeCommit`の環境の準備を行っていきます。