てくなべ (tekunabe)

ansible / network automation / 学習メモ

[Ansible] Azure Virtual Machines を generalize(一般化)する

はじめに

azure.azcollection.azure_rm_virtualmachineモジュールの generalized オプションを利用すると、VMgeneralize できます。

Azure CLI でいう az vm generalizePowerShell でいうSet-AzVm -Generalizedです。(後述しますが、VMの状態遷移の挙動が微妙に異なります)

このモジュールは多機能でオプションも多く、モジュールの公式ドキュメント上も generalize する例も掲載されていなかったため、試行錯誤してしまいました。

せっかくですのでまとめておきます。

  • 動作確認環境
    • ansible 5.1.0 (ansible-core 2.12.1)
    • azure.azcollection collection 1.10.0
    • VM の OS は Windows

基本形

Widows の VMsysprep /generalize /oobe /shutdown して、OSがシャットダウンされたあとに実行するシナリオです。そのため VM の状態は「停止済み」の想定です。「停止済み (割り当て解除)」ではありません。

generalize するタスクは以下のとおりです。

    - name: generalize vm
      azure.azcollection.azure_rm_virtualmachine:
        resource_group: "{{ resource_name }}"
        name: "{{ vm_name }}"
        started: false      # 起動状態にしない
        generalized: true   # generalize する

generalized: true が一番のポイントです。started: false は、VMを起動状態にしない指定です。

実行後、az vm get-instance-view で状態を確認すると code: OSState/generalized が表れます。

$ az vm get-instance-view -g rg-test -n win01 --query instanceView.statuses -o yaml
- code: OSState/generalized       # ここが増える
  displayStatus: VM generalized   # ここが増える
  level: Info                     # ここが増える
  message: null                   # ここが増える
  time: null                      # ここが増える
- code: ProvisioningState/succeeded
  displayStatus: Provisioning succeeded
  level: Info
  message: null
  time: '2021-12-29T08:35:21.523725+00:00'
- code: PowerState/stopped        # stopped のまま
  displayStatus: VM stopped
  level: Info
  message: null
  time: null

azure.azcollection.azure_rm_virtualmachine_info モジュールで状態を取得すると、power_stategeneralized となりました。

VMの状態と azure_rm_virtualmachine モジュールの generalized: true の関係まとめ

azure_rm_virtualmachine モジュールの generalized: true を指定するだけで、必ず generalized されるとは限りません。事前の VM の状態や started オプションの指定の仕方によって、generalized: true が効かないパターンがあります。モジュールのコードデバッグしながら実行して検証した結果を、以下のようにまとめました。

パターン 事前のVMの状態 started オプションの値 動作 事後のVMの状態 備考
1 実行中 true (デフォルト) VMを停止して、generalizeする 停止済み、generalized VM課金状態が続く
2 実行中 false VMを停止するのみ 停止済み 実質 generalized: true が効かない
3 停止済み true (デフォルト) VMを起動するのみ 実行中 実質 generalized: true が効かない
4 停止済み false VMを停止済みのまま、generalizeする 停止済み、generalized 前述のタスクの想定ケース、VM課金状態が続く
5 停止済み (割り当て解除) true (デフォルト) VMを起動するのみ 実行中 実質 generalized: true が効かない
6 停止済み (割り当て解除) false VMを停止済みにして(割当解除ではなく)、generalizeする 停止済み、generalized 課金されない状態からされる状態に遷移する
  • 状態についての補足
    • 「停止済み」はOS側でシャットダウン操作(sysprep/shutdown も同様)したのみの状態で、課金され続ける
    • 「停止済み (割り当て解除) 」は VM としての課金はされない状態(ストレージはまた別)
    • 詳細は「Azure Virtual Machines の状態と課金状態

パターン4が、前述のタスクの実行する想定ケースです。

generalized: true が効かないパターンがある(パターン2、3、5)

パターン2、3、5 については、generalized: true を指定しても効きません。generalized するかどうかの判定が後ろの方にあり、その前に、実行してたら停止、停止してたら実行のような判断がされます。

課金されない状態から課金される状態に遷移するパターンがある(パターン6)

パターン6については、意外な結果で注意が必要だと思いました。generalize はされますが、VMとしては課金されない状態である「停止済み (割り当て解除)」から、課金される「停止済み」に遷移するためです。

az vm generalizeSet-AzVm -Generalized は「停止済み (割り当て解除)」のまま、または「停止済み」のまま generalize するので、この点が異なります。)

モジュール内で、generalize すると判定されると power_off_vm()generalize_vm()が順に呼び出されます。power_off_vmの実行の段階で、「停止済み (割り当て解除)」から、「停止済み」に遷移しました。

generalize と deallocate をするプレイブック

これまでの結果かから、generalize が有効なパターンでは実行後に「停止済み」になることが分かりました。VM 課金を止めるためには「停止済み (割り当て解除)」状態にする必要があります。deallocate です。deallocate には azure.azcollection.azure_rm_virtualmachine モジュールで allocated: false を指定します。

プレイブックとしては、generalize するタスク、deallocate するタスクのように分けて定義します。基本形と同じく「停止済み」から実行する想定です。

---
- hosts: localhost
  connection: local
  gather_facts: false

  tasks:
    - name: vm generalized
      azure.azcollection.azure_rm_virtualmachine:
        resource_group: "{{ resource_name }}"
        name: "{{ vm_name }}"
        started: false
        generalized: true

    - name: vm deallocate
      azure.azcollection.azure_rm_virtualmachine:
        resource_group: "{{ resource_name }}"
        name: "{{ vm_name }}"
        allocated: false

deallocate まで実行したあとの状態は以下の通りです。

% az vm get-instance-view -g rg-test -n win01 --query instanceView.statuses -o yaml
- code: OSState/generalized         # generalize された
  displayStatus: VM generalized
  level: Info
  message: null
  time: null
- code: ProvisioningState/succeeded
  displayStatus: Provisioning succeeded
  level: Info
  message: null
  time: '2021-12-30T02:54:28.537511+00:00'
- code: PowerState/deallocated      # deallocate された
  displayStatus: VM deallocated
  level: Info
  message: null
  time: null

azure.azcollection.azure_rm_virtualmachine_info モジュールで状態を取得すると、power_stategeneralized となりました。この状態確認だと、一度 generalized すると、stopped なのか deallocated なのか分からないのが少々難点でしょうか。

なお、azure.azcollection.azure_rm_virtualmachine モジュールの1つのタスクで generalized: trueallocated: false を両方指定してしまうと、deallocate されるだけで、generalize されないのでご注意ください。

まとめ

azure.azcollection.azure_rm_virtualmachine モジュールは便利ですが、オプションの数が多いと依存関係がパッと見でわからなかったのがつまずいたポイントでした。

今回はデバッグしながら挙動を確認しました。

参考: [Ansible] 「つまずき Ansible 【Part19】モジュールのコードをデバッグしたい」ふりかえり - てくなべ (tekunabe)

補足

azure.azcollection.azure_rm_image モジュール で、generalize していない VM からイメージを作成しようとすると、Generalized になってっている必要がある、という旨のエラーが表示されます。

fatal: [localhost]: FAILED! => {"changed": false, "msg": "Error creating image win-image01 - Azure Error: OperationNotAllowed\nMessage: The operation 'Create Image' requires the Virtual Machine 'win01' to be Generalized."}

ポータル上で、generalize してない VM からイメージ作成をするときはエラーが出ずに、途中で「仮想マシンが一般化されました 」と表示されます。暗黙的に行われるようです。