てくなべ (tekunabe)

ansible / network automation / 学習メモ

[Ansible/Azure] azure_rm インベントリプラグインでインベントリ名を仮想マシン名に合わせる

はじめに

azure.azcollection.azure_rm というインベントリプラグインは、AzureのVMの情報を動的に取得できます。

対象のリソースグループ名やグループ化の仕方、変数の持ち方などをYAMLで定義します。

このインベントリプラグイン利用時に、インベントリ名(inventory_hostname)を仮想マシン名にする方法を紹介します、

  • 動作確認環境
    • ansible 5.1.0 (ansible-core 2.12.1)
    • azure.azcollection collection 1.10.0

デフォルトでは?

デフォルトでは、インベントリ名は 仮想マシン名_4文字 のようになります。

仮想マシン名は、仮想マシンをポータルで作成する画面でいう「仮想マシン名」です。

4文字のところは、仮想マシンのID(/subscriptions/サブスクリプションID/resourceGroups/リソースグループ名/providers/Microsoft.Compute/virtualMachines/仮想マシン名)をもとにしたハッシュ値の最初の4文字です。

リソースグループが別だと同じ仮想マシン名が許容されるので、競合させないための工夫のようです。

ですが、設計上全リソースグループ内で仮想マシン名をユニークにしていたり、include_vm_resource_groups パラメーター で対象リソースグループを制限している場合などでは仮想マシン名は競合せず、インベントリ名を仮想マシン名に合わせたいケースもあるかもしれません。

仮想マシン名に合わせる機能は、インベントリプラグインとしては後から追加され、実装上はAzure rm legacy hostnames と呼んでいたようです。

インベントリ名を仮想マシン名にするサンプル

インベントリ名を仮想マシン名にするには、以下のように、plain_host_names オプションに、true を指定します。

  • inventory_azure_rm.yml
plugin: azure.azcollection.azure_rm
include_vm_resource_groups: # (参考) 対象リソースグループ名、デフォルトはすべて
  - ansible_azure
plain_host_names: true      # インベントリ名を仮想マシン名にする
conditional_groups:         # (参考) 動的なグループ定義
  windows: "'WindowsServer' in image.offer"

なお、ファイル名は公式ドキュメントにあるように、ファイル名を azure_rm.(yml|yaml) で終わるようにします。

実行例

インベントリ名を書くにするために、debug モジュールで、"I am {{ inventory_hostname }}" を出力するだけのPlaybookを実行します。ここでは、ansible01仮想マシン名です

$ ansible-playbook -i inventory_azure_rm.yml debug.yml

PLAY [all] ************************************************************************************************************

TASK [debug inventory_hostname] ***************************************************************************************
ok: [ansible01] => {
    "msg": "I am ansible01"
}

PLAY RECAP ************************************************************************************************************
ansible01                  : ok=1    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0 

インベントリ名を確認するだけなら、Playbookは実行せずに ansible-inventory コマンドを利用すのが便利です。

$ ansible-inventory -i inventory_azure_rm.yml --graph
@all:
  |--@ungrouped:
  |--@windows:
  |  |--ansible01

なお、デフォルトである plain_host_names: false の場合は、以下のようにインベントリ名は 仮想マシン名_4文字 になります

$ ansible-inventory -i inventory_azure_rm.yml --graph
@all:
  |--@ungrouped:
  |--@windows:
  |  |--ansible01_d165

別解

以下のように、hostaname オプションで name を指定することでも、結果的には同じ挙動になります。

# ...略...
hostnames:  
   - name

より意図を示すためには、plain_host_names オプションを利用するほうが良いかと思います。

[Ansible] 2021年のAnsibleリリースまとめ

はじめに

2020年は、ansible-base (現 ansible-core) と collection に分かれての配布が始まったことが大きな出来事でした。

2021年も、ansible-core も、コミュニティが選別したcollectionをセットにしたパッケージもそれぞれアップデートされていきました。

また、 Red Hat Ansible Automation Platform (AAP) 2.X のリリースも大きかったと思います。

2021年の重要だと感じたリリースを簡単にまとめます。

2月

ansible 3.0.0 リリース

バージョン体系を変更してからの最初のリリース。

3月

AWX 18.0.0 リリース

docker-compose ではなく、AWX Operator によるインストール方法が優先に。

個人的にはこれ以降インストールを試していません・・。

github.com

4月

ansible-core 2.11.0 リリース

zenn.dev

5月

ansible 4.0.0 リリース

ansible-core は 2.11 系。

zenn.dev

7月

Red Hat Ansible Automation Platform 2.0 (Early Access) リリース

Ansible Tower が Automation Controler に改称。 その他、実行環境がコンテナベースになるという大きなアーキテクチャ変更。 関連ツールは、ansible-nabigator、ansible-runner、ansible-builder。

11月

ansible-core 2.12.0 リリース

コントロールノードで Python 3.8 以上が必要に。

zenn.dev

12月

ansible 5.0.0 リリース

ansible-core は 2.12 系。 zenn.dev

Red Hat Ansible Automation Platform 2.1 リリース

関連 www.ansible.com

おわりに

2022年はどのようなことが起こるのでしょうか。

個人的には、コンテナベースの実行環境の利用が進んでいくような予感がしています。

[Ansible] ansible-plyabook コマンドでインベントリを複数指定する

はじめに

ansible-playbook コマンドの -i オプションは複数のインベントリを指定できます。

ansible-playbook -i inventory01.ini -i inventory02.ini playbook.yml 

この記事ではかんたんなサンプルと実行例を紹介します。

  • 動作確認環境
    • ansible 5.1.0 (asnble-core 2.12.1)

サンプル

インベントリファイルの用意

インベントリファイルを2つ用意します。分かりやすくするため、1つめに group01、2つめに group02 を定義します。

  • inventory01.ini
[grouop01]
router101
router102
  • inventory02.ini
[grouop02]
server201
server202

合計、4台のホストです。

Playbookの実行

hosts: all にした適当なPlaybookを実行します。

$ ansible-playbook -i inventory01.ini -i inventory02.ini test.yml

PLAY [all] **************************************************************************

TASK [debug inventory_hostname] *****************************************************
ok: [router101] => {
    "msg": "I am router101"
}
ok: [router102] => {
    "msg": "I am router102"
}
ok: [server201] => {
    "msg": "I am server201"
}
ok: [server202] => {
    "msg": "I am server202"
}

PLAY RECAP ***************************************************************************
router101  : ok=1  changed=0  unreachable=0  failed=0  skipped=0  rescued=0  ignored=0   
router102  : ok=1  changed=0  unreachable=0  failed=0  skipped=0  rescued=0  ignored=0   
server201  : ok=1  changed=0  unreachable=0  failed=0  skipped=0  rescued=0  ignored=0   
server202  : ok=1  changed=0  unreachable=0  failed=0  skipped=0  rescued=0  ignored=0   

2つのインベントリに含まれる4台のホスト分が実行されました。

Playbookを実行せずにホスト一覧を確認する

Playbookを実行せずとも、ホストの確認だけなら他の方法もあります。

ansible-playbook--list-hosts オプションを利用する方法。こちらでは、Playbookが hosts: all なので、全部出力されます。

$ ansible-playbook -i inventory01.ini -i inventory02.ini test.yml --list-hosts

playbook: test.yml

  play #1 (all): all    TAGS: []
    pattern: ['all']
    hosts (4):
      server201
      router101
      router102
      server202

ansible-inventory というインベントリ確認用のコマンドも便利です。

@all:
  |--@grouop01:
  |  |--router101
  |  |--router102
  |--@grouop02:
  |  |--server201
  |  |--server202
  |--@ungrouped:

少し応用(スタティックとダイナミックの組み合わせ)

先程ほどのサンプルは、スタティックなインベントリを複数していました。スタティックとダイナミックの組み合わせもできます。

ここでは、AzureのVMの情報を利用できる azure.azcollection.azure_rmインベントリプラグインを利用するインベントリを、2つめに指定します。

$ansible-inventory -i inventory01.ini -i inventory_azure_rm.yml --graph 
@all:
  |--@grouop01:
  |  |--router101
  |  |--router102
  |--@rg_ansible_azure:
  |  |--azure01_7192
  |--@ungrouped:
  |--@windows:
  |  |--azure01_7192

rg_ansible_azurewindows グループが、azure.azcollection.azure_rm インベントリプラグインで取得したものです。

[Ansible/Azure] 含まれるリソースまるごとリソースグループを削除する

はじめに

azure.azcollection.azure_rm_resourcegroupモジュールは、Azureのリソースグループを作成したり削除したりできます。

削除したいときは、state: absent を指定しればよいですが、リソースグループ内にリソースが含まれていると削除できません。

どうやろうかなと思っていたのですが、force_delete_nonempty: true を併用すればいいようでした。

取り扱いにはとても注意が必要ですが、検証時などで作ったり削除したりするときは便利です。

かんたんなサンプルで紹介します。

  • 動作検証環境
    • ansible 5.1.0 (ansible-core 2.12.1)
    • azure.azcollection collection 1.10.0

サンプルPlaybook

以下の通り、state: absentforce_delete_nonempty: true を併用します。

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

  tasks:
    - name: delete resource group (with included resources)
      azure.azcollection.azure_rm_resourcegroup:
        name: ansible_azure
        state: absent
        force_delete_nonempty: true

おためし

削除前

ansible_azure というリソースグループを対象とします。削除前はいくつかのリソースが含まれている状態です。

$ az resource list -g ansible_azure -o yaml  
- changedTime: '2021-12-30T07:28:38.834843+00:00'
  # ...(略)...
  resourceGroup: ANSIBLE_AZURE
  # ...(略)...
  type: Microsoft.Compute/disks
- changedTime: '2021-12-30T07:30:38.798837+00:00'
  #. ..(略)...
  resourceGroup: ansible_azure
  type: Microsoft.Compute/virtualMachines
- changedTime: '2021-12-30T07:28:05.527039+00:00'
  # ...(略)...
  resourceGroup: ansible_azure
  type: Microsoft.Network/networkInterfaces
- changedTime: '2021-12-30T07:29:51.633415+00:00'
  # ...(略)...
  resourceGroup: ansible_azure
  # ...(略)...
  type: Microsoft.Network/networkSecurityGroups
- changedTime: '2021-12-30T07:30:01.912170+00:00'
  resourceGroup: ansible_azure
  # ...(略)...
  type: Microsoft.Network/publicIPAddresses
- changedTime: '2021-12-30T07:29:45.735572+00:00'
  # ...(略)...
  resourceGroup: ansible_azure
  # ...(略)...
  type: Microsoft.Network/virtualNetworks

削除実行

先程のPlaybookを実行します。

$ ansible-playbook -i localhost, delrg.yml 

PLAY [localhost] ***********************************************************************************

TASK [delete resource group (with included resources)] *********************************************
changed: [localhost]

PLAY RECAP *****************************************************************************************
localhost   : ok=1    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   

数分かかりました。

削除後

リソースグループごと削除されました。

$ az resource list -g ansible_azure -o yaml         
(ResourceGroupNotFound) Resource group 'ansible_azure' could not be found.
Code: ResourceGroupNotFound
Message: Resource group 'ansible_azure' could not be found.

[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 からイメージ作成をするときはエラーが出ずに、途中で「仮想マシンが一般化されました 」と表示されます。暗黙的に行われるようです。

Tera Term の接続画面のホスト欄はユーザー名やコメントなども指定できる

ホスト欄でいろいろな指定

タイトルどおりですが、Tera Term の接続画面のホスト欄にはユーザー名やコメントなどを指定できます。

この部分です。

f:id:akira6592:20211220105430p:plain
接続画面のホスト入力欄

たとえば以下のような指定ができます。

指定 意味
admin@192.168.0.254 192.168.0.254 にユーザー admin でログインする(認証画面のユーザー名にadminが予め入る)
admin@192.168.0.254 ; コアスイッチ 同、コメント コアスイッチ 付き
admin@192.168.0.254 /F=TERATERM_green.INI ; コアスイッチ 同、設定ファイル TERATERM_green.INI の指定

履歴に残るので便利です。ユーザー名を履歴に残してよいかどうかは運用ルールにもよると思いますが。

応用?(接続先ごとに背景色を変える)

/F=設定ファイル を指定することで、ホストと設定ファイルをセットで指定でき「サーバーAへの接続時は黒の背景、Bのときは緑の背景」といったこともできますね。

f:id:akira6592:20211220110314p:plain:w450
接続先ごとに背景色を変える

知ったきっかけ

ユーザー名あたりは、たまたまできたのがきっかけですが、コマンドライン文字列が使えるということだそうです。ありがとうございます。

ttssh2.osdn.jp

思い返してみると、デフォルトの履歴がこのようにいろんな例が載っていましたね。

f:id:akira6592:20211220105028p:plain
デフォルトの履歴でいろんなパターン

[Ansible] FQCN で省略系のモジュール名を指定できる仕組み(cisco.ios.ios_config を cisco.ios.config で)

この記事は、Ansible Advent Calendar 2021 (Adventar版) 20日目の記事です。

はじめに

先日 Ansible もくもく会に参加させていただきました。(参加レポート

ネットワーク編のコンテンツでは、たびたび cisco.ios.config のような、少しモジュール名を省略した形でされていました。

---
- name: snmp ro/rw string configuration
  hosts: cisco
  gather_facts: no

  tasks:

    - name: ensure that the desired snmp strings are present
      cisco.ios.config:
        commands:
          - snmp-server community ansible-public RO
          - snmp-server community ansible-private RW

Exercise 2 - First Ansible Playbook | workshops より)

例えば上記の例では、cisco.ios.ios_config ではなく、cisco.ios.config という指定をしています。

この省略方法ができるのは知っていたのですが、実際ここまで指定してる例ははじめて見ました。

リダイレクトの定義ファイルがある

cisco.ios.config という省略した指定でも利用できるのは、cisco.ios.ios_config に対してリダイレクトする定義があるためです。

リダイレクトの対応はコレクションの meta/runtime.yml というファイルに定義されています。他のモジュールにも同様の定義があります。

github.com

---
requires_ansible: ">=2.9.10"
plugin_routing:
# ...(略)...
  modules:
# ...(略)...
    ios_bgp_global:
      redirect: cisco.ios.ios
    bgp_global:
      redirect: cisco.ios.ios
    ios_command:
      redirect: cisco.ios.ios
    command:
      redirect: cisco.ios.ios
    ios_config:
      redirect: cisco.ios.ios
    config:
      redirect: cisco.ios.ios
    ios_facts:
      redirect: cisco.ios.ios
    facts:
      redirect: cisco.ios.ios
# ...(略)...

モジュールが非推奨になったときにもこのリダイレクトの定義を使うようですが、ここでは deprecation という指定はありません。

このような省略系が指定できる仕組みは、meta/runtime.yml 追加時のプルリクでは、short name module redirection と表現されています。

コレクション内でのリダイレクト定義なので、コレクションを指定する cisco.ios までは必要です。さすがに config ではモジュールが一意に定まりません。

他のコレクションにもリダイレクト定義がある

この仕組は cisco.ios コレクション固有ではなく、コレクション自体の仕組みです。

そのため、他のコレクションにもこのようなリダイレクト定義があったり(なかったり)します。

どっちを使うか

コレクションが導入される前は階層がなくフラットだったため、ios_config のように、モジュール名自体に名前空間的なプレフィックスが必要でした。コレクションの導入によって、cisco.ios のような名前空間ができたため、これで十分 ios 向けであることが判別できます。このため、モジュール名の ios_ の必要性が低くなり冗長であるという見方があって、cisco.ios.config という省略形でも指定できるようにしよう、という経緯なのかなという予想です。

私としては、初見の人にやさしいであろうという理由で省略形ではなく cisco.ios.ios_config という表記の方が良いかなと思います。