CentOS7コンテナにインストールしたAnsibleを使ってAzureVMを操作してみた（ダイナミックインベントリ使用）

## はじめに
AnsibleでAzureVMを操作できるか検証を行う機会があったので、その時の手順を自分メモとして残しておきたいと思います。

## やったこと
- コントロールノードを CentOS7（Dockerコンテナ）
- ターゲットノードをAzureVM（CentOS7）2台
- ターゲットノードにNginxをインストールを行う
- Ansibleの「ダイナミックインベントリ」を使用する
![qiita.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/759747/c8f6d0f3-920a-93a9-a310-33d44b7759a6.png)


以下のAzure 公式ドキュメントの内容をベースに進めていきました。
[チュートリアル - Ansible を使用して Azure リソースの動的インベントリを構成する | Microsoft Docs](https://docs.microsoft.com/ja-jp/azure/developer/ansible/dynamic-inventory-configure?tabs=azure-cli)

## コントロール側のバージョン確認
 - Docker Desktop for Windows (Version 3.3.1) / Docker Engine v20.10.5
 - CentOS 7.9.2009
 - Ansible 2.10.8
 - Azure CLI  2.22.1

## 1. 事前準備
コントロールノード側に各種ツールをインストールしていきます。

### ・Ansibleインストール
Microsoft Azure 公式ドキュメントを見ながらインストールを行います。

参考URL：[クイック スタート - Azure CLI を使用して Ansible を構成する | Microsoft Docs](https://docs.microsoft.com/ja-jp/azure/developer/ansible/install-on-linux-vm?tabs=ansible#install-ansible-on-the-virtual-machine)

### ・Azure CLI インストール
Azure CLIを使用して、リソースグループ作成・削除およびVM構築を行います。
こちらも、Microsoft Azure 公式ドキュメントを見ながらインストールを行います。

参考URL：[Linux での Azure CLI の手動インストール | Microsoft Docs](https://docs.microsoft.com/ja-jp/cli/azure/install-azure-cli-linux?pivots=dnf)

## 2. SSH鍵を作成する
AzureVMにログインする際に使用する鍵を作成します。
パスフレーズ無しで`ansible_rsa`というファイル名で作成します。

```bash:コンテナ内
ssh-keygen -m PEM -t rsa -b 2048 -f ~/.ssh/ansible_rsa -N ""
```

## 3. Azure リソースグループを作成する
AzureVMが属するリソースグループを`myAnsibleRG`という名前で作成します。

```bash:コンテナ内
az group create --name myAnsibleRG --location japaneast
```

## 4. AzureVMを作成する
このVMが「ターゲットノード」ということになります。
SSH鍵は前工程で作成した`ansible_rsa`を指定します。

```bash:コンテナ内
# 1台目
az vm create --resource-group myAnsibleRG --name myAnsibleVM01 --image OpenLogic:CentOS:7.7:latest --size Standard_B1s --admin-username azureuser --ssh-key-values ~/.ssh/ansible_rsa.pub

# 2台目
az vm create --resource-group myAnsibleRG --name myAnsibleVM02 --image OpenLogic:CentOS:7.7:latest --size Standard_B1s --admin-username azureuser --ssh-key-values ~/.ssh/ansible_rsa.pub
```

## 5. AzureVMにタグを付ける
タグ付けした方のVMにだけNginxをインストールさせようとしているため、1台目のVMにタグ付けを行います。

```bash:コンテナ内
az resource tag --tags Ansible=nginx --id /subscriptions/<YourAzureSubscriptionID>/resourceGroups/myAnsibleRG/providers/Microsoft.Compute/virtualMachines/myAnsibleVM01
```

## 6. 80番ポートを開ける
Nginxがインストールされたかを確認するため、1台目のVMの80番ポートを事前に開けておきます。

```bash:コンテナ内
az vm open-port --port 80 --resource-group myAnsibleRG --name myAnsibleVM01
```

## 7. Azure の資格情報（クレデンシャル）ファイルの作成
AnsibleにAzureの資格情報を提供するためのローカルのクレデンシャルファイルを作成します。
セキュリティの観点から**環境変数を定義する方法**が推奨されますが、今回は検証目的のためクレデンシャルファイルを作成しました。

まず`credentials`という名称のファイルを作成します。

```bash:コンテナ内
mkdir ~/.azure
vi ~/.azure/credentials
```

ファイルの中身は以下の通りです。
※誤って公開することがないようにご注意ください。

```bash:コンテナ内
[default]
subscription_id=<your-subscription_id>
client_id=<security-principal-appid>
secret=<security-principal-password>
tenant=<security-principal-tenant>
```

## 8. ダイナミックインベントリの生成
インベントリはテキストファイルにターゲットノードの情報を定義して管理することもできるのですが、接続先のIPアドレスやホスト名がほぼ固定であるオンプレミス環境ならば問題ありませんが、クラウドでは頻繁にインスタンス（EC2やAzureVM）の追加や削除が発生するため、そのたびにインベントリファイルを更新するのは現実的ではありません。

そこで、スクリプトを使用してAPI経由でダイナミック（動的）にターゲットノードの情報を取得するのが**ダイナミックインベントリ**と呼ばれる仕組みです。

ダイナミックインベントリは`azure_rm`という名称で終わる必要のあるYAMLファイルになります。

今回は`myazure_rm.yml`として保存しています。

```yaml
plugin: azure_rm
include_vm_resource_groups:
- myAnsibleRG
auth_source: auto

keyed_groups:
- prefix: tag
  key: tags
```

## 9. VMへの接続テスト
ソースグループ内のVMをpingしてみます。

```bash:コンテナ内
ansible all -i ./myazure_rm.yml -m ping --private-key=~/.ssh/ansible_rsa -u azureuser
```

以下のような結果が表示され、VM2台ともに接続できていることが分かります。

```bash:コンテナ内
myAnsibleVM01_500a | SUCCESS => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    },
    "changed": false,
    "ping": "pong"
}
myAnsibleVM02_0c1a | SUCCESS => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    },
    "changed": false,
    "ping": "pong"
}
```

## 10. 設定済みのインベントリを確認する
設定済みのインベントリを確認します。

```bash:コンテナ内
ansible-inventory -i myazure_rm.yml --graph
@all:
  |--@tag_Ansible_nginx:
  |  |--myAnsibleVM01_500a
  |--@ungrouped:
  |  |--myAnsibleVM02_0c1a
```

`@tag_Ansible_nginx`を指定することで、1台目のVMへの接続をテストすることもできます。

```bash:コンテナ内
ansible -i ./myazure_rm.yml -m ping --private-key=~/.ssh/ansible_rsa -u azureuser tag_Ansible_nginx
```

## 11. プレイブックを作成する
Nginxをインストールするためのプレイブックを`nginx.yml`として作成します。

```yaml:コンテナ内
---
- name: Install and start Nginx on an Azure virtual machine
  hosts: all
  become: yes
  tasks:

  - name: Add Nginx Yum Repository
    yum_repository:
      name: nginx
      description: nginx repo
      baseurl: http://nginx.org/packages/mainline/centos/7/$basearch/
      owner: root
      gpgcheck: no
      enabled: yes
      state: present

  - name: Install nginx
    yum: name=nginx state=present
    notify:
    - start nginx

  handlers:
    - name: start nginx
      service: name=nginx state=started
```

## 12.Nginxのインストールと動作確認
プレイブックを実行して Nginx のインストールを行います。
1台目のVMにのみインストールできることを確認するため`--limit`を指定しています。

```bash:コンテナ内
ansible-playbook -i ./myazure_rm.yml --private-key=~/.ssh/ansible_rsa -u azureuser nginx.yml --limit=tag_Ansible_nginx
```

<br>
Nginxがインストールされたことを確認します。
![qiita_nginx.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/759747/81925471-8a94-fb8f-4b2a-4e3a2eec8b33.png)

また、2台目のVMにログインして Nginx がインストールされていないことを確認します。

```bash:AzureVM2台目内
[azureuser@myAnsibleVM02 ~]$ nginx -V
-bash: nginx: command not found
```

## 13. リソースのクリーンアップ
検証に使用したAzureリソースたちの後片付けを行って検証終了です。

```bash:コンテナ内
az group delete --name myAnsibleRG
```

## 【おまけ】検証中にハマったところ
接続テストの際にエラーが出てしまい軽くハマってしまいました。

```bash:コンテナ内
# これだとエラー
ansible -i ./myazure_rm.yml -m ping tag_Ansible_nginx
myAnsibleVM01_500a | UNREACHABLE! => {
    "changed": false,
    "msg": "Failed to connect to the host via ssh: Permission denied (publickey,gssapi-keyex,gssapi-with-mic).",
    "unreachable": true
}
```

今回の検証ではSSH鍵を`ansible_rsa`という名称で作成していたので、その鍵を明示的に指定する必要があります。

```bash:コンテナ内
# SSH鍵を明示的に指定する
ansible -i ./myazure_rm.yml -m ping --private-key=~/.ssh/ansible_rsa tag_Ansible_nginx
myAnsibleVM01_500a | FAILED! => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    },
    "changed": false,
    "module_stderr": "Shared connection to 20.xxx.xxx.xxx closed.\r\n",
    "module_stdout": "Please login as the user \"azureuser\" rather than the user \"root\".\r\n\r\n",
    "msg": "MODULE FAILURE\nSee stdout/stderr for the exact error",
    "rc": 0
}
```

それでもまだエラーが出るのですが、`azureuser`でログインするようにメッセージが出ているため、azureuserを指定するようにしてみます。

```bash:コンテナ内
# SSH鍵を明示的に指定しつつ、azureuserでログインする
ansible -i ./myazure_rm.yml -m ping --private-key=~/.ssh/ansible_rsa -u azureuser tag_Ansible_nginx
myAnsibleVM01_500a | SUCCESS => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    },
    "changed": false,
    "ping": "pong"
}
```

ようやく、エラーが解消されました。
以上です。