[Ansible]Cloudflare Zero Trust配下のAAP設定をGitLabで管理①～Playbook作成～

本記事は以下2部構成で作成しています。

- [[Ansible]Cloudflare Zero Trust配下のAAP設定をGitLabで管理①～Playbook作成～](https://qiita.com/miyuk/items/24e2afee90ffe61c2b93)(本記事)
- [[Ansible]Cloudflare Zero Trust配下のAAP設定をGitLabで管理②～Cloudflare・GitLab設定～](https://qiita.com/miyuk/items/d0d21bd0175b875aebe1)

# はじめに

皆様は、Ansible Automation Platform(AAP)・AWXのジョブテンプレート設定などはコードで管理していますか？

筆者にも言えることですがAAP内で利用するPlaybook自体のコードはGitで管理していてもAAP側の設定はGUIが備わっているので手作業でやることが多いです。

しかし、検証・本番Playbookを管理したり複数チームが入ってくるとAAPの設定はカオスになって手作業で管理することは難しくなります。

そこで今回は、AAPの設定パラメータをGitLabで管理して設定内容を自動で機器に設定するようにしてみたのでその内容を紹介します。

ここまでの内容としてはn番煎じの内容なので、AAPをCloudflare Zero Trust(以降Cloudflareという)配下に置き、GitLabからのアクセスのみを通すようにしました。

全体の構成は以下の通りです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/335255/bd756a0c-89a1-dde0-00e1-e4ae70758cb6.png)

本記事では、AAPのPlaybook作成部分(図の①)のみとし、
Cloudflare Zero Trustを使ってGitLabからセキュアにWebhookを利用する部分(図の②、③)は別記事として投稿します。

# 環境
- Ansible Automation Platform: 2.2
- ansible.controller collection: 4.4.0

# 構築

## ①AAP設定Playbook・Job Template作成

AAPに設定を行うためのPlaybook作成とそれを動かすJob Templateを作成します。

### Playbook作成

AAP設定を行うコレクションは[ansible.controllerコレクション](https://console.redhat.com/ansible/automation-hub/repo/published/ansible/controller)を使用します。

こちらのコレクションはAAPサブスクリプションがないと見れないですが、同様の仕様として[awx.awxコレクション](https://docs.ansible.com/ansible/latest/collections/awx/awx/index.html)があるのでそちらも参照ください。

当初、GitLab RunnerからAnsibleを直接動かしてAAP設定を行おうとしていましたが、Cloudflareを含めて使おうとしたときに技術的にポリシーを通すことが出来ませんでした。
そのため、GitLab RunnerはAAPにWebhookを送ってAAP側でJob Templateを実行するようにしました。

設定対象は運用で変更が多いであろう以下オブジェクトにしています。

- Inventory
- Inventory Sources
- Projects
- Execution Environments
- Job Templates
- Workflow Job Templates

設定対象のデータはモジュール説明を見て以下のようなvarsとして定義します。
かなり面倒なので`ansible.controller.export`モジュールを使って必要部分だけ取り込み、dictの順番を変更して使いました。

```yaml:vars/projects.yml
---
projects:
  - name: PJ_DEV
    description: Develop Project
    organization: Default
    credential: GitLab SCM
    scm_url: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
    scm_branch: develop
    scm_update_on_launch: true
  - name: PJ_PRD
    description: Production Project
    organization: Default
    credential: GitLab SCM
    scm_url: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
    scm_branch: main
    scm_update_on_launch: true
```
Playbookは以下の通りで、先程定義したデータを読み込んでloopでオブジェクトを作成します。

```yaml:configure_aap.yml
---
- name: Configure AAP
  hosts: localhost
  gather_facts: false
  vars_files:
    - vars/projects.yml
    - vars/inventory.yml
    - vars/inventory_sources.yml
    - vars/execution_environments.yml
    - vars/job_templates.yml
    - vars/workflow_job_templates.yml
  tasks:
    - name: Configure projects
      ansible.controller.project:
        state: present
        name: "{{ item.name }}"
        description: "{{ item.description | default(omit) }}"
        organization: "{{ item.organization | default(omit) }}"
        credential: "{{ item.credential | default(omit) }}"
        default_environment: "{{ item.default_environment | default(omit) }}"
        scm_type: "{{ item.scm_type | default(omit} }"
        scm_url: "{{ item.scm_url | default(omit) }}"
        scm_branch: "{{ item.scm_branch | default(omit) }}"
        scm_refspec: "{{ item.scm_refspec | default(omit) }}"
        scm_update_on_launch: "{{ item.scm_update_on_launch | default(omit) }}"
        scm_clean: "{{ item.scm_clean | default(omit) }}"
        scm_delete_on_update: "{{ item.scm_delete_on_update | default(omit) }}"
        scm_track_submodules: "{{ item.scm_track_submodules | default(omit) }}"
        scm_update_cache_timeout: "{{ item.scm_update_cache_timeout | default(omit) }}"
      loop: "{{ projects }}"

    - name: Configure EE
      ansible.controller.execution_environment:
    　・・・
```


### Job Template作成

次にAAP側でPlaybookを実行できるようにJob Templateを作成します。

まず、AAPを操作するためのトークンを以下手順で作成します。

1. `アプリケーション`より、認証付与タイプが`リソース所有者のパスワードベース`のオブジェクトを作成する
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/335255/b2680d82-e992-8e23-2333-1a967c3fe660.png)
2. `ユーザ設定`>`トークン`より、先ほど作成したアプリケーションの`Write`権限トークンを作成する
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/335255/d5038eb8-1654-531b-a9c4-a2c8e9e01440.png)

3. 発行されたトークンの`トークン`を認証情報として登録する
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/335255/7bfcb5d9-9d4c-a415-5968-0632eb965853.png)

次に作成したトークンをJob Templateで使えるように認証情報タイプを作成し、環境変数経由でトークン認証できるようにします。

```yaml:inputs
fields:
  - id: CONTROLLER_HOST
    type: string
    label: Controller Host URL
  - id: CONTROLLER_OAUTH_TOKEN
    type: string
    label: Controller API Token
    secret: true
  - id: CONTROLLER_VERIFY_SSL
    type: boolean
    label: Verify SSL
required:
  - CONTROLLER_HOST
  - CONTROLLER_OAUTH_TOKEN
  - CONTROLLER_VERIFY_SSL
```

```yaml:injectors
env:
  CONTROLLER_HOST: '{{ CONTROLLER_HOST }}'
  CONTROLLER_VERIFY_SSL: '{{ CONTROLLER_VERIFY_SSL }}'
  CONTROLLER_OAUTH_TOKEN: '{{ CONTROLLER_OAUTH_TOKEN }}'
```

あとは、適当なインベントリと合わせてJob Templateを作成します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/335255/05f4ec10-b88a-4412-a65a-1fd14570fcbc.png)

### Webhook待ち受け設定

最後にAAPのWebhookからJob Templateを実行できるようにします。

Webhookの設定は、オプションの`Webhookの有効化`チェックボックスをつけることで待ち受け可能になります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/335255/3c9680d3-82a4-335d-f1a0-12ba722b9082.png)

Webhookサービスは、`GitHub`と`GitLab`の2パターンがありますので今回は`GitLab`を選択します。

認証情報を入力することで実行完了後にGitLab側に結果通知を出来ますが、今回のやり方の場合GitLabの純粋なWebhookを使わないためそのままにします。

これでWebhookが使えるようになったのでcurlを使い、Job Template作成時に出てきた`Webhook URL`と`Webhookキー`を含めて実行します

```bash
curl <Webhook URL> -X POST \
 -H "Content-Type: application/json" \
 -H "X-Gitlab-Token: <Webhookキー>" \
 -d "{\"test\": \"test\"}"
```

結果に以下が出ればWebhook成功です。
```json
{"message":"Job queued."}
```

Webhookはキューとして受け付けただけを返すのみで、Job実行結果はAAP側を見る必要があります。
AAP側でJob結果で`成功`であればOKです。

これでサーベイや複雑なワークフローを含んだWorkflow Templateを作成することが出来ました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/335255/52d7f73b-f4ab-c3c1-865e-ae5b8851157c.png)


次に２回目実行しようとしましたが、以下ワーニングが出て実行されませんでした。

```json
{"message":"Webhook previously received, aborting."}
```

これは、AAP側にてWebhookが複数回実行しないように[リクエストボディーをハッシュ化してGUIDとして管理](https://github.com/ansible/awx/blob/2a1dffd36346e31a5eb9bd9e5770a3ea12827c4f/awx/api/views/webhooks.py#L200)しているからです。

そのため、curlでPOSTするときはリクエストボディーにランダムなJSONを入力する必要があります。
GitLabで実行する際は、コミットハッシュをつけるのが適当かと思います。

# おわりに

AAPの自動構築Playbook・Job Templateを使ってみて、パラメータをコピペして検証環境・本番環境を簡単に作ることができるのはかなり実用的でした。

Job TemplateのWebhook機能も初めて使いましたが、Webhookキーによる簡易認可機能もあり外部公開しても問題なさそうに思いました。
しかし、AAPの役割を考えると脆弱性を突かれたときのリスクがあるためそのまま公開するのは避けたほうがよさそうです。
この辺りは、次回のCloudflare Zero Trustを使って解決します。

一方、Webhookは対象サービスがGitHubとGitLabしかないため、汎用的に使う場合はAPIでジョブ制御したほうが良いとも感じました。
特に、ボディのリクエストを毎回変える必要があることと、実行ジョブの結果を確認できないのは引っ掛かりポイントでした。

# 参考リンク

- https://docs.ansible.com/automation-controller/4.0.0/html_ja/userguide/webhooks.html
    - AAP Webhook説明ページ
- https://console.redhat.com/ansible/automation-hub/repo/published/ansible/controller
    - ansible.controllerコレクションのページ(要Red Hatアカウントログイン)
- https://docs.ansible.com/ansible/latest/collections/awx/awx/index.html
    - awx.awxコレクション説明ページ
- https://github.com/ansible/awx/blob/2a1dffd36346e31a5eb9bd9e5770a3ea12827c4f/awx/api/views/webhooks.py#L200
    - webhookでGUID計算しているソースコード部分