Ansibleを使用したAzure VM環境構築

# はじめに
クラウド上にVM(仮想マシン)を構築する場合に使用するIaC(Infrastructure as Code)ツールには、クラウドベンダーに依存せずに利用できる[Terraform](https://www.terraform.io/)がありますが、主にサーバー構成管理で使用する[Ansible](https://docs.ansible.com/ansible/2.9_ja/index.html)でもクラウド上にVM構築を行うことが可能です。今回はAnsibleを使用してMicrosoft Azureクラウド上にVMを構築してみましたので、作成したサンプルのPlaybookと、Ansibleで構築する場合の考慮点などをご紹介します。

# 検証環境
RedHat Enterprise Linux 8.6
Ansible 2.9.27
python 3.6.8

# 今回Azure上に構築する環境
下記のリソースを全て東日本リージョンに作成するものとします。
* リソースグループ
* VNET(仮想ネットワーク)
  * IPアドレス範囲：10.0.0.0/16
* サブネット
  * IPアドレス範囲：10.0.1.0/24
* NSG(ネットワーク・セキュリティー・グループ)
  * 外部からのSSH接続(ポート:22)を許可するようにします。
* NIC(ネットワーク・インタフェース・カード)
  * VMインスタンスに少なくとも1つは必要です。
* Public IP
  * VMインスタンスにssh接続する際に必要になります。
* Linux VMインスタンス
  * Linuxのディストリビューションには、Ubuntu Server 18.04 LTSを使用するものとします。

当環境は、Terraformでも一度構築してみたことがあります。Terraformによる構築例は下記をご参照ください。

https://qiita.com/duelist2020jp/items/6b78adae9a242f3c5935



# 事前準備
以下のリンク先を参考に実施します。
[Ansible Microsoft Azureガイド](https://docs.ansible.com/ansible/2.9_ja/scenario_guides/guide_azure.html)

* Azure SDKモジュールのインストール
Ansibleは、Azure Resource Managerと対話することで、Azureクラウド上に環境構築を行うことができます。そのため、検証環境のLinux上にAzure SDKモジュールを下記コマンドを実行してインストールします。
```
# pip3 install 'ansible[azure]'
```
* サービスプリンシパルを使用したAzure認証
こちらは、Terraformを使用する場合と同様です。方法は下記リンク先にまとめていますので、ご参照ください。リンク先の検証環境はWindowsですが、Linuxの場合も同様です。

https://qiita.com/duelist2020jp/items/c2b99ecfba2196ae65fc

# 作成したPlaybook
下記のAnsible Azure Moduleの紹介サイトを参考に作成しました。
[Cloud Modules Azure](https://docs.ansible.com/ansible/2.9_ja/modules/list_of_cloud_modules.html#azure)

上記リンク先を元に今回作成したPlaybookは下記のものになります。Playbook名は、azure_vm_create.yamlとしておきます。

```yaml:azure_vm_create.yaml
- hosts: localhost
  gather_facts: false

  vars:
    rg_name: My-RG
    location_name: japaneast
    net_secgrp_name: TestVM-Security-Group
    vnet_name: Test-VNET
    subnet_name: Test-Public-Subnet
    public_ip_name: Test-Public-IP
    nic_name: Test-NIC

  tasks:

    - name: Create a resource group
      azure_rm_resourcegroup:    
        name: "{{rg_name}}"
        location: "{{location_name}}"
        state: present
        tags:
          Name: Test

    - name: Create a Virtual Network
      azure_rm_virtualnetwork:
        name: "{{vnet_name}}"
        resource_group: "{{rg_name}}"
        location: "{{location_name}}"
        address_prefixes_cidr: "10.0.0.0/16"
        state: present
        tags:
          Name: Test

    - name: Create a network security group
      azure_rm_securitygroup:
        name: "{{net_secgrp_name}}"
        resource_group: "{{rg_name}}"
        location: "{{location_name}}"
        rules:
          - name: sg-ssh22
            access: Allow
            protocol: "Tcp"
            priority: 300
            direction: Inbound
            source_port_range: "*"
            source_address_prefix: "*"
            destination_port_range: 22
            destination_address_prefix: "*"
        state: present
        tags:
          Name: Test-SG  

    - name: Create a subnet
      azure_rm_subnet:
        name: "{{subnet_name}}"
        resource_group: "{{rg_name}}"
        virtual_network_name: "{{vnet_name}}"
        address_prefix_cidr: "10.0.1.0/24"
        security_group:
          name: "{{net_secgrp_name}}"
          resource_group: "{{rg_name}}"
        state: present

    - name: Create a public IP
      azure_rm_publicipaddress:
        name: "{{public_ip_name}}"  
        resource_group: "{{rg_name}}"
        location: "{{location_name}}"
        allocation_method: Dynamic
        state: present
    
    - name: Create a Network Interface Card
      azure_rm_networkinterface:
        name: "{{nic_name}}"
        resource_group: "{{rg_name}}"
        location: "{{location_name}}"
        ip_configurations:
          - name: internal
            private_ip_allocation_method: Dynamic
            public_ip_address_name: "{{public_ip_name}}" 
        subnet_name: "{{subnet_name}}"
        virtual_network: "{{vnet_name}}"
        security_group:
          name: "{{net_secgrp_name}}"
          resource_group: "{{rg_name}}"
        state: present
        tags:
          Name: Test

    - name: Create a Virtual Machine 
      azure_rm_virtualmachine:
        name: Test-VM
        resource_group: "{{rg_name}}"
        location: "{{location_name}}"
        admin_username: adminuser
        admin_password: XSw2#edc
        network_interface_names: "{{nic_name}}"
        virtual_network_name: "{{vnet_name}}"
        vm_size: Standard_A1_v2
        os_type: Linux
        image:
          publisher: Canonical
          offer: UbuntuServer
          sku: 18.04-LTS
          version: latest
        os_disk_caching: ReadWrite
        managed_disk_type: Standard_LRS
        state: present
        started: yes
        tags:
          Name: Test
```
# Ansible Playbookによる環境作成
* azure_vm_create.yamlファイルのあるディレクトリ上で、作成したplaybook(azure_vm_create.yaml)を指定して、下記のansible-playbookコマンドを実行します。
  * ansible-playbook azure_vm_create.yaml
* コマンドの実行結果で、<font color="Red">**failed**</font>の項目がないことを確認します。
* 問題なく環境作成できているか否かは、実際にAzureポータルログオンして確認するか、Azure CLIを使用して確認します。

# 作成したAzure VMインスタンスへの接続確認
今回VMインスタンスに付与されたPublic IPは、20.89.45.15です。こちらに対して、ログオン用ユーザーID(adminuser)、ログオン用パスワードでssh接続を実施します。
```
$ ssh adminuser@20.89.45.15
The authenticity of host '20.89.45.15 (20.89.45.15)' can't be established.
ECDSA key fingerprint is SHA256:pWHOK5PIUbLlVZMdHjV62p8atgcpiIalVWLaCKPKXDM.
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
Warning: Permanently added '20.89.45.15' (ECDSA) to the list of known hosts.
adminuser@20.89.45.15's password: 
Welcome to Ubuntu 18.04.6 LTS (GNU/Linux 5.4.0-1078-azure x86_64)

 * Documentation:  https://help.ubuntu.com
 * Management:     https://landscape.canonical.com
 * Support:        https://ubuntu.com/advantage

 System information disabled due to load higher than 1.0

0 updates can be applied immediately.



The programs included with the Ubuntu system are free software;
the exact distribution terms for each program are described in the
individual files in /usr/share/doc/*/copyright.

Ubuntu comes with ABSOLUTELY NO WARRANTY, to the extent permitted by
applicable law.

To run a command as administrator (user "root"), use "sudo <command>".
See "man sudo_root" for details.

adminuser@Test-VM:~$
```
無事、Azure VMインスタンスに接続できました！
# Ansibleで環境破棄をする場合の考慮点
Terraformの場合は、構成ファイルを1個作成しておけば、<font color="Blue">terraform destroy</font>コマンドを一発実行するだけで簡単に環境破棄を行うことが可能です。しかし、残念ながらAnsibleの場合は、環境破棄を行うためのPlaybookを別途作成する必要があります。

AnsibleのPlaybook内のstateパラメータは、<font color="LimeGreen">state: </font><font color="DeepSkyBlue">present</font>ならリソースを作成する、<font color="LimeGreen">state: </font><font color="DeepSkyBlue">absent</font>の場合にはリソースを削除するという意味があります。ですので、環境破棄するのなら全てのリソースに対して<font color="LimeGreen">state: </font><font color="DeepSkyBlue">absent</font>を指定したPlaybookを用意するだけで良いかというと、そう単純な話ではありません。AnsibleのPlaybookは、Playbookの<font color="Blue">tasks</font>内で記述したタスクを上から順に実施していきますので、処理の順序に対する考慮も必要になります。

そのため、あまり手間をかけずに簡単に環境破棄を行うためのサンプルPlaybookを作成しました。Playbook名は、azure_vm_delete.yamlとしておきます。
```yaml:azure_vm_delete.yaml
- hosts: localhost
  gather_facts: false
  
  vars:
    rg_name: My-RG
    location_name: japaneast
    vm_name: Test-VM

  tasks:

    - name: Delete Virtual Machine Instance and associated resources
      azure_rm_virtualmachine:
        name: "{{vm_name}}"
        resource_group: "{{rg_name}}"
        remove_on_absent: all_auto_created
        state: absent


    - name: Delete a resource group
      azure_rm_resourcegroup:    
       name: "{{rg_name}}"
       location: "{{location_name}}"
       force_delete_nonempty: yes
```
当Playbookは、以下のステップで環境破棄を行います。
1. VM作成時に自動的に生成されたリソースを、VM削除と同時に削除する。
1. リソースグループ内に残存するリソースを、リソースグループ自体を削除することでまとめてクリーンアップする。

# Ansible Playbookによる環境破棄
* azure_vm_delete.yamlファイルのあるディレクトリ上で、作成したplaybook(azure_vm_delete.yaml)を指定して、下記のansible-playbookコマンドを実行します。
  *  ansible-playbook azure_vm_delete.yaml
* コマンドの実行結果で、<font color="Red">**failed**</font>の項目がないことを確認します。
* 問題なく環境破棄ができたか否かは、実際にAzureポータルログオンして確認するか、Azure CLIを使用して確認します。

# さいごに
今回は以下を実施して問題ないことを確認しました。
* Ansible Playbookを使用して、Microsoft Azureクラウド上にVMインスタンスを構築・破棄できること。
* 構築したVMインスタンスにssh接続できること。

Terraform、Ansible双方での環境構築をしてみた個人的な感想を下記にまとめてみました。(あくまでも主観です。)

* IaCツールに普段からAnsibleを使用しているような場合には、新たにPlaybookの書き方などをマスターするための学習コストも低いと思いますので、Ansibleで構築してみるのも良いかなと思いました。
* 一方で、IaCツール自体をほとんど触ったことすらない方の場合には、構成ファイルを1個用意さえすればコマンド一発で環境構築・環境破棄が可能なTerraformをマスターするのが良いかなと思いました。