[Ansible]AAP内のPlaybookからgit clone, pushを行う

この記事は[エーピーコミュニケーションズAdvent Calendar 2023](https://qiita.com/advent-calendar/2023/ap-com) 17日目の投稿です。

# はじめに

Ansible Automation Platform(以下AAPという)環境ではよくGitも併せて利用して様々なデータの保管場所にすることが多いかと思います。
AAPで利用するProjectやInventoryなどはAAPの機能でGUIから参照することができますが、Playbook実行時の作業証跡などを参照したい場合やデータ追加したい場合はPlaybook内で個別操作する必要があります。

Playbook内でGitを操作する方法として、Ansible Tower時代はTowerサーバ内のLinuxユーザがGit権限を持っていればそれを利用することができました。
しかし、AAPになってからはEEを使用するため、Git権限を持つ環境を事前準備する必要が出てきました。

AAPからGit操作する方法としては以下やり方が考えられます。

- AAPでGit接続情報を持ったうえでEEに事前準備してGit操作を行う
- AAPからGit利用できるサーバに接続してそこからgit操作を行う
- EE作成時にGit接続情報を含めてビルドして利用する(EE内に機密情報があるので非推奨)

この中で1つ目の`AAPでGit接続情報を持ったうえでEEに事前準備してGit操作を行う`方法を紹介します。

※Gitサーバとの認証方法は大別して`HTTPS方式`と`SSH方式`がありますがここでは、`SSH方式`で説明します。Git操作自体は同じなので認証情報を変えてあげれば`HTTPS方式`でも対応可能です。


# 事前準備

## SSH認証鍵の作成とGitサーバへの公開鍵登録

事前にTerratermなどでSSH認証鍵ペア(`id_rsa.pub`, `id_rsa`)を作成して、Gitサーバ側に`id_rsa.pub`を登録します。

## AAPに秘密鍵登録

次に先ほど作成したSSH秘密鍵(`id_rsa`)をAAPの認証情報として利用できるようにします。

### 秘密鍵用カスタム認証タイプ作成

AAPでは、`ソースコントロール`という認証タイプがすでに存在しますが、こちらはAAPシステムがProjectなどを取得するためのもののため、今回は利用できません。
代わりに、カスタム認証タイプを作成します。

カスタム認証タイプで以下内容のものを作成します。
これにより、AAPはJob Template実行時にEEのランダムな場所にSSH秘密鍵情報が記載されたファイルを作成したうえでファイル名を`git_ssh_private_key`変数に格納します。

```yaml:入力の設定
fields:
  - id: ssh_private_key
    type: string
    label: SSH秘密鍵
    secret: true
    multiline: true
required:
  - ssh_private_key
```

```yaml:インジェクターの設定
file:
  template.ssh_private_key: '{{ ssh_private_key }}'
extra_vars:
  git_ssh_private_key: '{{ tower.filename.ssh_private_key }}'
```

### SSH認証情報オブジェクトを作成

先ほど作成したカスタム認証タイプをもとに認証情報オブジェクトを作成します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/335255/4abbf3d6-7307-383e-b9d2-576ccf110fee.png)

注意点として、秘密鍵フォーマットは最後に空白行が必要なのでコピペする際は最終行までコピーしてください。
私は、ここでうまくいかず苦戦しました。

# 実行

# Playbook

以下作業を行うPlaybookを作成しました。

1. Gitアクセス環境の準備
1. Git clone
1. 適当なファイルを作成
1. Git push

Gitアクセス環境の準備では、SSH Configの設定とGitホストのフィンガープリント登録を行っています。
もろもろファイル操作をしていますが、EEが対象だと毎回変更フラグがついてしますので`failed_when: false`をつけています。

フィンガープリントの追加は、SSH Configのところで`StrictHostKeyChecking `をつけてもよさそうです。

Git用のモジュールは参照関連のものしかないため、Git commit & Git pushはコマンドモジュールを使っています。

ファイルの内容は以下のとおり。

```yaml:playbook.yml
- name: Git access playbook
  hosts: localhost
  gather_facts: false
  vars:
    git_host: <Gitサーバホスト>
    git_repo: <レポジトリ名>
    git_branch: main
    git_dir: ./ansible-test
    git_username: Test User
    git_email: testuser@local
    git_commit_comment: Test Commit

  tasks:
    - name: Set ssh_config
      ansible.builtin.blockinfile:
        path: ~/.ssh/config
        mode: "0644"
        create: true
        block: |
          Host {{ git_host }}
            IdentityFile {{ git_ssh_private_key }}

    - name: Get git host fingerprints
      ansible.builtin.command: "ssh-keyscan -H {{ git_host }}"
      register: ssh_keyscan_result
      changed_when: false

    - name: Set known_hosts
      ansible.builtin.lineinfile:
        path: ~/.ssh/known_hosts
        mode: "0644"
        create: true
        line: "{{ item }}"
      loop: "{{ ssh_keyscan_result.stdout_lines }}"
      changed_when: false

    - name: Clone git repository
      ansible.builtin.git:
        repo: "git@{{ git_host }}:{{ git_repo }}"
        dest: "{{ git_dir }}"
        version: "{{ git_branch }}"

    - name: Set git_config
      community.general.git_config:
        repo: "{{ git_dir }}"
        scope: local
        name: "{{ item.name }}"
        value: "{{ item.value }}"
      loop:
        - name: user.name
          value: "{{ git_username }}"
        - name: user.email
          value: "{{ git_email }}"
      changed_when: false

    - name: Create test file
      ansible.builtin.copy:
        content: "{{ now() }}"
        dest: "{{ git_dir }}/test"
        mode: "0644"

    - name: Execute git add all files
      ansible.builtin.command: "git add -A -v"
      args:
        chdir: "{{ git_dir }}"
      register: git_add_result
      changed_when: git_add_result.stdout | length > 0

    - name: Check respository is changed
      ansible.builtin.command: "git diff --staged --name-only"
      args:
        chdir: "{{ git_dir }}"
      register: git_diff_result
      changed_when: false

    - name: Set is_changed
      ansible.builtin.set_fact:
        is_changed: "{{ git_diff_result.stdout | length > 0 }}"

    - name: Execute git commit
      ansible.builtin.command: "git commit -m '{{ git_commit_comment }}'"
      args:
        chdir: "{{ git_dir }}"
      changed_when: true
      when:
        - is_changed

    - name: Execute git push
      ansible.builtin.command: "git push"
      args:
        chdir: "{{ git_dir }}"
      changed_when: true
      when:
        - is_changed
```

### Job Template

Job Templateには、前述のPlaybookと認証情報をいれます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/335255/c824473c-9e3d-8d02-8c69-0f18602336a5.png)

これを実行することでPlaybook内でGit認証情報を受け取ってGit操作をすることができます。

# まとめ

今更なネタではあるのですが、なかなか例が見当たらなかったため投稿しました。

認証情報をAAP側で制御することができるので検証・本番でSSH鍵を使い分けることができ置き換えも楽にすることができます。

案件で使う場合はGit push、Git pull用のRoleを作っておくことでメンテナンスしやすく再利用性が高くなるためおすすめです。

# 参考リンク

https://docs.ansible.com/automation-controller/latest/html/userguide/credential_types.html

https://gitlab-docs.creationline.com/ee/user/ssh.html

https://docs.ansible.com/ansible/latest/collections/ansible/builtin/git_module.html