Azure上に、WindowsのVMを用いた環境を構築する必要があったので、Ansibleで構築を一気にやってしまうことにした。今回は、リソースグループから作成して、VMの作成とWinRMの有効化、各VMへの接続、設定までをまとめてみる。 Playbookのサンプルはこちらに置いてあるが、Azure上にAD環境を構築する内容になっているのでそのまま流す場合は注意。また、この情報は2018年6月時点のもので、以後変わっていく可能性が高い点についても注意してもらいたい。

1. Azure-CLIの設定

まず、AnsibleをキックするマシンでAzure-CLIを利用できるようにする必要がある。 Azure-CLI 2.0ではサービスプリンシパルを利用することでmicrosoftアカウントユーザでもログイン情報を保持できるようになった(ちょっと前まではログイン情報保持できなかった)ようだ。とてもありがたい(aws-cliとかではもともとできてたので、使い勝手が同じ方がいい)。

Azure-CLI(azコマンド)はaptやbrewからインストールできるし、公式ドキュメントも充実しているのでそちらを参照のこと。また、サービスプリンシパルの設定についてもこちらに記載があるので参考になるだろう。

2. リソースグループ~仮想ネットワークの作成

まずリソースグループや仮想ネットワークなどを作成しないと始まらないので、それらを作成する。 リソースグループや仮想ネットワーク、セキュリティグループ、Subnet、ストレージアカウントを作成する場合、以下のようなPlaybookを作成する。

---
- name: Create Azure ResourceGroup.
  azure_rm_resourcegroup:
      name: "{{ azure_rg_name }}"
      location: "{{ azure_rg_location }}"

- name: Create Azure VirtualNetwork.
  azure_rm_virtualnetwork:
      resource_group: "{{ azure_rg_name }}"
      name: "{{ azure_vn_name }}"
      address_prefixes_cidr: "{{ azure_vn_cidr }}"
      dns_servers:
        - "8.8.8.8"

- name: Create Security Group.
  azure_rm_securitygroup:
    resource_group: "{{ azure_rg_name }}"
    name: "{{ azure_sg_name }}"
    purge_rules: yes
    rules:
      - name: AllowSpSSH
        protocol: Tcp
        source_address_prefix: "{{ from_global_ip }}"
        destination_port_range: 22
        access: Allow
        priority: 100
        direction: Inbound

- name: Update Security Group.
  azure_rm_securitygroup:
    resource_group: "{{ azure_rg_name }}"
    name: "{{ azure_sg_name }}"
    rules:
      - name: AllowSpRDP
        protocol: Tcp
        source_address_prefix: "{{ from_global_ip }}"
        destination_port_range: 3389
        access: Allow
        priority: 101
        direction: Inbound
      - name: AllowSpWinrm
        protocol: Tcp
        source_address_prefix: "{{ from_global_ip }}"
        destination_port_range: 5985
        access: Allow
        priority: 102
        direction: Inbound
      - name: AllowSpWinrmSSL
        protocol: Tcp
        source_address_prefix: "{{ from_global_ip }}"
        destination_port_range: 5986
        access: Allow
        priority: 103
        direction: Inbound

- name: Create Azure VirtualNetwork Subnet.
  azure_rm_subnet:
    resource_group: "{{ azure_rg_name }}"
    virtual_network_name: "{{ azure_vn_name }}"
    name: "{{ azure_vn_subnet_name }}"
    address_prefix_cidr: "{{ azure_vn_subnet_cidr }}"
    security_group_name: "{{ azure_sg_name }}"

- name: Create storage account
  azure_rm_storageaccount:
    name: "{{ azure_storage_name }}"
    account_type: "{{ azure_storage_type }}"
    resource_group: "{{ azure_rg_name }}"

変数で定義している箇所については、以下のような使い方になっている。 実際に使用する場合は適宜書き換えてもらいたい。

  • azure_rg_name ... リソースグループ名
  • azure_rg_location ... リソースグループのロケーション
  • azure_vn_name ... 仮想ネットワーク名
  • azure_vn_cidr ... 仮想ネットワークのCIDR
  • azure_sg_name ... セキュリティグループ名
  • from_global_ip ... アクセス元のグローバルIP(任意)
  • azure_vn_subnet_name ... サブネット名
  • azure_vn_subnet_cidr ... サブネットのCIDR
  • azure_storage_name ... ストレージアカウント名
  • azure_storage_type ... ストレージアカウントのタイプ

3. VMの作成(WinRM設定含む)

基盤が作成できたら、WindowsVMの作成用Playbookを定義する。 複数台作成できるようにするため、roleを用いる。roleのディレクトリ構成やらについては、ここで説明しなくてもそのへんにいっぱい情報あるので割愛(サンプルではroleになってるので、そちらを参考にしてもらえればいいかと)。

./site.yml
--- - name: Create VMs hosts: localhost connection: local vars_files: - vars/vars.yml - vars/azure_vms.yml roles: - azure_vms
./roles/azure_vms/tasks/main.yml
--- - name: Create public IP azure_rm_publicipaddress: name: "{{ item.name }}-IP" allocation_method: Static resource_group: "{{ azure_rg_name }}" with_items: "{{ azure_vms }}" - name: Create NIC azure_rm_networkinterface: name: "{{ item.name }}-NIC" subnet: "{{ item.subnet_name }}" public_ip_name: "{{ item.name }}-IP" private_ip_address: "{{ item.private_ip_address }}" private_ip_allocation_method: "{{ item.private_ip_allocation_method }}" virtual_network: "{{ azure_vn_name }}" resource_group: "{{ azure_rg_name }}" security_group_name: "{{ azure_sg_name }}" with_items: "{{ azure_vms }}" - name: Create VM azure_rm_virtualmachine: name: "{{ item.name }}" vm_size: "{{ item.vm_size }}" storage_blob: "{{ item.name }}-OS.vhd" admin_username: "{{ item.username }}" admin_password: "{{ item.password }}" os_type: "{{ item.os_type }}" os_disk_caching: "{{ item.os_disk_caching }}" network_interfaces: "{{ item.name }}-NIC" image: offer: "{{ item.image_offer }}" publisher: "{{ item.image_publisher }}" sku: "{{ item.image_sku }}" version: "{{ item.image_version }}" storage_account: "{{ azure_storage_name }}" resource_group: "{{ azure_rg_name }}" with_items: "{{ azure_vms }}" - name: Add VM Extension azure_rm_virtualmachine_extension: name: "{{ item.name }}-extension" location: "{{ azure_rg_location }}" resource_group: "{{ azure_rg_name }}" virtual_machine_name: "{{ item.name }}" publisher: Microsoft.Compute virtual_machine_extension_type: CustomScriptExtension type_handler_version: 1.8 settings: { "fileUris": ["https://raw.githubusercontent.com/ansible/ansible/devel/examples/scripts/ConfigureRemotingForAnsible.ps1"], "commandToExecute": "powershell.exe -ExecutionPolicy Unrestricted -File ConfigureRemotingForAnsible.ps1" } auto_upgrade_minor_version: true with_items: "{{ azure_vms }}"

with_items(指定した要素の内容をループしてitem.XXXで参照できる)で各VMの情報を取得しているので、ちょっとわかりにくいかもしれない。 各item.XXXの内容については、サンプルのこちらのファイルを見てもらったほうがイメージがつくかも。

ここでのポイントは、ラストにあるazure_vm_virtualmachine_extensionで、これが無いとWinRMを有効にできないので注意(後処理で対象のVMにAnsibleから接続できなくなる)。

4. 各VMへの接続チェック

最後に、ちゃんとVMにAnsibleから接続できることを確認するためのPlaybookについて抜粋。

./site.yml
- name: Add hosts type hosts: localhost connection: local vars_files: - vars/vars.yml - vars/azure_vms.yml tasks: - include: tasks/add_hosts.yml with_items: "{{ azure_vms }}" loop_control: loop_var: user - name: Ping hosts: server vars: ansible_user: "{{ azure_server_user }}" ansible_password: "{{ azure_server_pass }}" ansible_port: 5986 ansible_connection: winrm ansible_winrm_server_cert_validation: ignore winping:
./tasks/add_hosts.yml
--- - name: Get public ip list. azure_rm_publicipaddress_facts: resource_group: '{{ azure_rg_name }}' name: '{{ user.name }}-IP' register: azure_ip - name: Add new instance to host group add_host: hostname: "{{ item.name | regex_replace('-IP','') }}" ansible_host: "{{ item.properties.ipAddress }}" groups: "server" with_items: "{{ azure_ip.ansible_facts.azure_publicipaddresses }}"

add_hostで作成したVMのPublic IPをhostに追加して、それを対象としてwinpingを実行している(Ansibleでログインできるかどうかをチェックしているので、WinRMが動いてないとここで失敗する)。 各VMへの接続チェックまでは行ったので、あとは個別に設定を流してあげればいいかなと(今回はそこまでやらないので、win_pingで疎通確認までにした)。

かなりやっつけだけど、細かい内容についてはSampleで上げてあるPlaybookを参考にしてもらえればと思う。