てくなべ (tekunabe)

ansible / network automation / 学習メモ

[Ansible] グループAとBの両方に所属しているホストを対象にする方法

■ はじめに

Ansible では、管理対象のホストをグループ化してインベントリファイルに定義できます。Playbook 内にグループを指定すると、指定したグループに所属するホストのみが対象になります。

では「グループAとBに両方に所属しているホスト」を対象にしたい場合はどのようにしたらよいでしょうか。 例えば以下のようなインベントリファイルがある場合、host02host03 を対象にしたい、というケースです。

[group_a]
host01
host02
host03

[group_b]
host02
host03
host04

この記事では、両方に所属しているホストを対象にする2つの方法を、簡単な例とともにご紹介します。

  • 動作確認環境: Ansible 2.3.0, 2.7.9


■ 方法1: play の hosts ディレクティブにグループを and 条件で指定する

Pkabbook内(のplay)の hosts ディレクティブには、複数の対象を and 条件で指定できます。この機能を利用する方法です。

参考: https://docs.ansible.com/ansible/latest/user_guide/intro_patterns.html

  • playbook
- hosts: group_a:&group_b
  gather_facts: no

  tasks:
    - name: debug test
      debug:
        msg: "I am {{ inventory_hostname }}."

上記のように group_a:&group_b と指定することで、group_a に所属、かつ group_b にも所属、という指定になります。

  • 実行例
$ ansible-playbook -i inventory test.yml

PLAY [group_a:&group_b] ****************************************************************

TASK [debug test] **********************************************************************
ok: [host02] => {
    "msg": "I am host02."
}
ok: [host03] => {
    "msg": "I am host03."
}

PLAY RECAP ******************************************************************************
host02                     : ok=1    changed=0    unreachable=0    failed=0    skipped=0
host03                     : ok=1    changed=0    unreachable=0    failed=0    skipped=0

両グループに所属している、host02host03 が対象となりました。

なお、--list-hosts オプションを付けると実処理をせずに対象ホストを確認できます。

$ ansible-playbook -i inventory test.yml --list-hosts

playbook: test.yml

  play #1 (group_a:&group_b): group_a:&group_b  TAGS: []
    pattern: [u'group_a:&group_b']
    hosts (2):
      host02
      host03


■ 方法2: -l オプションと組み合わせる

ansible-playbook コマンドには、対象ホストを絞り込む -l または --limit オプションがあります。この機能と、hosts ディレクティブを併用する方法です。

参考: https://docs.ansible.com/ansible/latest/cli/ansible-playbook.html#cmdoption-ansible-playbook-l

  • playbook
- hosts: group_a
  gather_facts: no

  tasks:
    - name: debug test
      debug:
        msg: "I am {{ inventory_hostname }}."

Playbook としては group_a を対象としています。

  • 実行例
$ ansible-playbook -i inventory test.yml -l group_b

PLAY [group_a] ***************************************************************************

TASK [debug test] ************************************************************************
ok: [host02] => {
    "msg": "I am host02."
}
ok: [host03] => {
    "msg": "I am host03."
}

PLAY RECAP *******************************************************************************
host02                     : ok=1    changed=0    unreachable=0    failed=0    skipped=0
host03                     : ok=1    changed=0    unreachable=0    failed=0    skipped=0

-l group_b で絞ることによって、結果的に両グループに所属している host02host03 が対象となりました。

--list-hosts オプションでも確認してみます。

$ ansible-playbook -i inventory test.yml -l group_b --list-hosts

playbook: test.yml

  play #1 (group_a): group_a    TAGS: []
    pattern: [u'group_a']
    hosts (2):
      host02
      host03


■ 方法3: when で group_names で判断する [2019/10/16 追記]

Play 単位ではなく、when が効く ロールや block 、タスクなどで使える方法です。

- hosts: all
  roles:
    - role: test
      when: "'group_a' in group_names and 'group_b' in group_names"
  tasks:
    - debug:
msg: In group_a and group_b
      when: "'group_a' in group_names and 'group_b' in group_names"

@satoh_fumiyasu さんにアイディアいただきました。ありがとうございます。


■ まとめ

複数のグループに所属しているホストを対象にする方法を2つご紹介しました。 ただ、インベントリとして新たにグループを定義したほうが、メンテナンス性が良いケースもあるかもしれません。必要に応じて使い分けるのが良いと思います。

[Ansible] Playbook(YAML)のための vim のインデント関連設定

■ はじめに

Ansible の Playbook を書いて試すときは、普段 VSCode を使っているため、あまり vim は使っていません。ですが、vim しか使えない環境に置かれたときのために vim で Playbook (YAML) を書くときに便利そうな設定を調べました。

f:id:akira6592:20190321135658g:plain
インデント関連設定をした vim で Playbook をかく

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


■ .vimrc の中身

今のところ、 .vimrc の中身は以下のようにしています。

set expandtab
set softtabstop=2
set shiftwidth=2
set autoindent

それぞれの設定について簡単に説明します。


set expandtab: Tab キーを押したときに Tab の代わりにスペースを入力する

省略系: set et

通常、Tab キーを押すと tab が入力されます。YAML のインデントには tab ではなくスペースを利用します。 そこで、set expandtab で、Tab キーを押したときに tab の代わりにスペースを入力されうように設定します。


set softtabstop=2: softtab(スペース)の幅をスペース2個にする

省略形: set sts

前述の set expandtab で有効にした Tab キーによるスペース入力の、スペースの数を指定します。 Playbook (YAML)としては、スペースが2個でも4個でも階層的に矛盾がなければ構わないのですが、私はいつもスペース2個なので set softtabstop=2 を設定します。


set shiftwidth=2: インデントレベルの変更時のスペースを2個にする

省略形: set sw=2

>><< などによるインデントレベルの変更時に使うスペースの数です。softtabstop の数と合わせて、set shiftwidth=2を設定します。

f:id:akira6592:20190321135112g:plain
インデントレベルの調整


set autoindent: 自動でインデントする

省略系: set ai

インデントされた状態の行をにカーソルがある状態で改行したときに、次の行で同じインデント位置を保つようにする設定です。

f:id:akira6592:20190321135042g:plain
自動インデント

この設定は少し注意が必要です。手入力時は便利ですが、すでにインデントされたテキストをコピペすると余計なインデントが入ってしまいます。

例えば以下のインデントされたテキストの場合。

- hosts: eos
  gather_facts: no

  tasks:
    - name: test
      eos_command:
        commands:
          - show running-config
      register: result

ペーストすると以下のように余計なインデントが入ってしまいます。これは正しいPlyabookではありません。

- hosts: eos
  gather_facts: no

    tasks:
        - name: test
              eos_command:
                      commands:
                                - show running-config
                                      register: result                                                             

対策は以下のとおりです。(ほかにもあるかもしれません。)

対策1. 自動インデントが不要なときだけ無効(set noautoindent / set noai)に設定する。 対策3. set paste でペーストモードにしてからペーストする。 対策3. そもそも普段は自動インデント無効にしておき、必要な時だけ有効(set autoindent / set ni)に設定する


■ まとめ

vim で Playbook (YAML)を書くときに便利そうな設定をまとめました。YAML では、インデントの数が情報構造に強く関わっているので、特にインデント周りの設定を好みに設定しておくと良いと思いました。

参考

参考にさせていただきました。ありがとうございます。 hatakazu.hatenablog.com

Ansible 公式ドキュメントでは、Ansible vim というプラグインが紹介されています。

docs.ansible.com

[Ansible] オフライン状態でモジュールの詳細をなど調べる方法(ansible-doc/ansible-config)

■ はじめに

Playbook を書いていく過程で、Ansible の公式ドキュメントを閲覧したり、インターネットで検索したりして、調べることが多いと思います。

しかし、もしインターネットにつながっていない状態で調べことをしたい時はどのようにしたらよいでしょうか。

この記事では、オフライン状態(ここではインターネット未接続状態のこと指します)で、Ansible について調べる方法をいくつかご紹介します。対応する公式ドキュメントのページもあわせてご紹介します。

動作確認環境: Ansible 2.7.8


■ モジュール

モジュールの詳細情報を知りたい

モジュール名は分かっているけれど、オプションの名前やサンプルを確認したい時に調べる方法です。

ansible-doc [モジュール名]
  • 実行例: ios_config モジュールの詳細を表示する
$ ansible-doc ios_config

 IOS_CONFIG    (/home/vagrant/ansible2780/lib/python2.7/site-packages/ansible/modules/network/ios/ios_confi

        Cisco IOS configurations use a simple block indent file syntax for segmenting
        configuration into sections.  This module provides an implementation for
        working with IOS configuration sections in a deterministic way.

(...略...)

EXAMPLES:

- name: configure top level configuration
  ios_config:
    lines: hostname {{ inventory_hostname }}

- name: configure interface settings
  ios_config:
    lines:
      - description test interface
      - ip address 172.31.1.1 255.255.255.0
    parents: interface Ethernet1

(...略...)

公式ドキュメントでは、こちらの ios_config モジュールのページを確認します。

モジュール名の一部からモジュール名を特定したい

モジュール名の一部しか分かっていないけれど、どのモジュールかを特定したい時に調べる方法です。

ansible-doc -l | grep [正規表現]
  • 実行例: ios_ から始まるモジュール名の一覧を表示する
$ ansible-doc -l | grep ^ios_
ios_banner                                           Manage multiline banners on Cisco IOS devices
ios_command                                          Run commands on remote devices running Cisco IOS
ios_config                                           Manage Cisco IOS configuration sections
ios_facts                                            Collect facts from remote devices running Cisco IO...
ios_interface                                        Manage Interface on Cisco IOS network devices
ios_l2_interface                                     Manage Layer-2 interface on Cisco IOS devices.
ios_l3_interface                                     Manage Layer-3 interfaces on Cisco IOS network dev...
ios_linkagg                                          Manage link aggregation groups on Cisco IOS networ...
ios_lldp                                             Manage LLDP configuration on Cisco IOS network dev...
ios_logging                                          Manage logging on network devices
ios_ping                                             Tests reachability using ping from Cisco IOS netwo...
ios_static_route                                     Manage static IP routes on Cisco IOS network devic...
ios_system                                           Manage the system attributes on Cisco IOS devices
ios_user                                             Manage the aggregate of local users on Cisco IOS d...
ios_vlan                                             Manage VLANs on IOS network devices
ios_vrf                                              Manage the collection of VRF definitions on Cisco ...

公式ドキュメントでは、モジュールリストから検索します。

ansible-doc -l のみの場合、モジュールの一覧を表示


■ コマンド

オプションを知りたい

あれするときのオプションは何だったかな?という時に調べる方法です。

$ [特にオプションを付けずにコマンドを実行、または -h オプション]
  • 実行例: ansible-playbook コマンドのヘルプを表示する
$ ansible-playbook -h
Usage: ansible-playbook [options] playbook.yml [playbook2 ...]

Runs Ansible playbooks, executing the defined tasks on the targeted hosts.

Options:
  --ask-vault-pass      ask for vault password
  -C, --check           don't make any changes; instead, try to predict some
                        of the changes that may occur
  -D, --diff            when changing (small) files and templates, show the
                        differences in those files; works great with --check
(...略...)

-h オプションなしで、単に ansible-playbook でも可

公式ドキュメントでは、ansible-playbook コマンド説明ページで確認します。(一覧はこちら


プラグイン

コネクションプラグインの一覧を表示したい

コネクションプラグインsshnetwork_cliなど)ってどんなのがあったかな?という時に調べる方法です。

ansible-doc -t connection -l
  • 実行例
$ ansible-doc -t connection -l
buildah      Interact with an existing buildah container
chroot       Interact with local chroot
docker       Run tasks in docker containers
(...略...)
netconf      Provides a persistent connection using the netconf protocol
network_cli  Use network_cli to run command on network appliances
(...略...)

公式ドキュメントでは、コネクションプラグインのリストを確認します。

コネクションプラグインの詳細を表示したい

コネクションプラグイン名は分かっているけれど、利用される変数名などの詳細を知りたい時に調べる方法です。

ansible-doc -t connection [コネクションプラグイン名]
  • 実行例: netconf コネクションプラグインの詳細を表示する
$ ansible-doc -t connection netconf
> NETCONF    (/home/vagrant/ansible2780/lib/python2.7/site-packages/ansible/plugins/connection/netconf.py)

        This connection plugin provides a connection to remote devices over the SSH
        NETCONF subsystem.  This connection plugin is typically used by network
        devices for sending and receiving RPC calls over NETCONF. Note this connection
        plugin requires ncclient to be installed on the local Ansible controller.

OPTIONS (= is mandatory):

- host
        Specifies the remote device FQDN or IP address to establish the SSH connection
        to.
        [Default: inventory_hostname]
        set_via:
          vars:
          - name: ansible_host
(...略...)

公式ドキュメントでは、netconf コネクションプラグインの説明ページを確認します。


■ 設定

項目名を知りたい

ansible.cfg環境変数で定義する設定項目名を知りたい時に調べる方法です。

ansible-config dump | grep [正規表現]
  • 実行例: timeout が含まれる設定項目名を表示する
$ ansible-config dump | grep -i timeout
CACHE_PLUGIN_TIMEOUT(default) = 86400
DEFAULT_GATHER_TIMEOUT(default) = 10
DEFAULT_TIMEOUT(default) = 10
PERSISTENT_COMMAND_TIMEOUT(default) = 10
PERSISTENT_CONNECT_RETRY_TIMEOUT(default) = 15
PERSISTENT_CONNECT_TIMEOUT(default) = 30

公式ドキュメントでは、設定一覧のページを確認します。


■ fact変数名の一部からfact変数名を特定したい

fact変数名の一部しか分かっていないけれど、どのfact変数かを特定したい時に調べる方法です。

ansible -i [インベントリ] [ターゲット] -m setup -a "filter=[パターン]" 

-m オプションを省略すると、すべてのfact変数の内容が表示されます

  • 実行例: localhost の fact変数で ipv4 が含まれるものを表示
$ ansible -i localhost, all -m setup -a "filter=*ipv4*" -c local
localhost | SUCCESS => {
    "ansible_facts": {
        "ansible_all_ipv4_addresses": [
            "172.17.0.1", 
            "10.0.2.15", 
            "172.16.0.9"
        ], 
        "ansible_default_ipv4": {
            "address": "10.0.2.15", 
            "alias": "eth0", 
            "broadcast": "10.0.2.255", 
            "gateway": "10.0.2.2", 
            "interface": "eth0", 
            "macaddress": "52:54:00:xx:xx:xx", 
            "mtu": 1500, 
            "netmask": "255.255.255.0", 
            "network": "10.0.2.0", 
            "type": "ether"
        }
    }, 
    "changed": false
}


■ まとめ

オフライン状態で、Ansible のモジュールやコネクションプラグイン、設定項目について調べる方法をご紹介しました。個人的には意外と調べる方法方があった、という印象を受けました。

もし、インターネットに接続できないシーンで調べごとをしたいときのめに、参考にしていただければ幸いです。

[Ansible] ループにリストの変数を渡すときの(私が)忘れがちな挙動差分

■ はじめに

ループするためにリストの変数を渡すときに、以下の2パターンを特に区別なく使ってしまうことがあります(私が)。

  • pattern1
- name: pattern1
    debug:
    msg: "{{ item }}"
    with_items: "{{ test_list }}"  # ここにリストの変数を渡す
  • pattern2
- name: pattern2
    debug:
    msg: "{{ item }}"
    with_items:
      - "{{ test_list }}"   # ここにリストの変数を渡す

どちらも結果は同じなのですが、内部の挙動が異なります。

この記事では、簡単な例で違いを説明します。


■ 動作確認

Playbook

以下のパターンのタスクを準備します。

pattern 説明
pattern1 with_items に直接リストを指定
pattern2 with_items の最初の要素にリストを指定
pattern3 loop に直接リストを指定
pattern4 loop の最初の要素にリストを指定

この中で一つだけ結果が異なるパターンがあります。

- hosts: localhost
  gather_facts: no

  vars:
    test_list:   # ループに使うリストの変数
      - a
      - b
      - c

  tasks:
    - name: pattern1
      debug:
        msg: "{{ item }}"
      with_items: "{{ test_list }}"
    
    - name: pattern2
      debug:
        msg: "{{ item }}"
      with_items:
        - "{{ test_list }}"

    - name: pattern3
      debug:
        msg: "{{ item }}"
      loop: "{{ test_list }}"

    - name: pattern4
      debug:
        msg: "{{ item }}"
      loop:
        - "{{ test_list }}"

実行結果

実行して確認します。

$ ansible-playbook -i localhost, looplist.yml

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

TASK [pattern1] ************************************************************
ok: [localhost] => (item=a) => {
    "msg": "a"
}
ok: [localhost] => (item=b) => {
    "msg": "b"
}
ok: [localhost] => (item=c) => {
    "msg": "c"
}

TASK [pattern2] ************************************************************
ok: [localhost] => (item=a) => {
    "msg": "a"
}
ok: [localhost] => (item=b) => {
    "msg": "b"
}
ok: [localhost] => (item=c) => {
    "msg": "c"
}

TASK [pattern3] ************************************************************
ok: [localhost] => (item=a) => {
    "msg": "a"
}
ok: [localhost] => (item=b) => {
    "msg": "b"
}
ok: [localhost] => (item=c) => {
    "msg": "c"
}

TASK [pattern4] ************************************************************
ok: [localhost] => (item=[u'a', u'b', u'c']) => {
    "msg": [
        "a",
        "b",
        "c"
    ]
}

PLAY RECAP ******************************************************************
localhost                  : ok=4    changed=0    unreachable=0    failed=0

解説

pattern1 -3 が一次元のリストとしてループしたのに対して、pattern4 だけ二次元のリストとしてループしました。

pattern4 では、リスト0番目の要素に ["a", "b", "c"] というリストが入っている状態です。

patterm 2 も構造上は二次元のリストですが、with_times がループ時に事前に、一次元のリストに flatten します。その結果、pattern1 や 3 と同じ結果になります。


■ まとめ

with_items を利用したループにおいて、同じ結果でも内部の挙動が異なる点をご紹介しました。

loop との違いや、リストが意図した通りに処理されているかは意識したほうが良いと思いました。

[Ansible] インベントリファイルを YAML 形式で書く

■ はじめに

Ansible のターゲットホストの情報を定義するインベントリファイルは、INI 形式の他にも YAML 形式でも定義できす。

この記事では、2つの例をとって ini と YAML を比較しながら YAML でのインベントリファイルの書き方を説明します。

  • 動作確認環境
    • Ansible 2.7.8, 2.3.0


■ 簡単な例

はずは簡単な例です。1つのグループに2つのホストが所属し、ホスト個別の変数、グループ共通の変数を定義します。

ini の場合

ini で書く場合は以下のような例です。

[junos]
junos01 ansible_host=172.16.1.1
junos02 ansible_host=172.16.1.2

[junos:vars]
ansible_network_os=junos
ansible_connection=netconf

ansible-inventory コマンドの --graph オプションでは、グループとホストの関係が以下のように表示さます。

$ ansible-inventory -i inventory01.ini --graph
@all:
  |--@junos:
  |  |--junos01
  |  |--junos02
  |--@ungrouped:

YAML の場合

YAML で書く場合は以下のようになります。

all:
  children:
    junos:   # junos グループ
      hosts: # junos グループのホスト
        junos01:
          ansible_host: 172.16.1.1  # ホスト変数
        junos02:
          ansible_host: 172.16.1.2  # ホスト変数
      vars: # junos グループ変数
        ansible_network_os: junos
        ansible_connection: netconf

以下のような特徴があります。

  • すべてのホストや、グループが暗黙的に所属する all グループをトップに定義する
  • グループの定義の配下には、所属させたいホストを hosts 、所属させたい子グループを children で定義する
  • ホスト変数は、ホスト名配下に定義する
  • グループ変数は vars 配下に定義する

対応

f:id:akira6592:20190310163459p:plain
ini と YAML の対応


■ 少し複雑な例

次に、簡単な例にもうひと工夫した少し複雑な例です。 先ほどとは、junos という一つのグループのみでしたが、同じレベルに ios グループを加えます。さらにこれら2つのグループの親グループ router も定義します。

別途、どのグループにも所属しないホスト web01 も定義します。

ini の場合

ini で書く場合は以下のような例です。

web01 ansible_host=127.16.0.1

[router:children]
junos
ios

[junos]
junos01 ansible_host=172.16.1.1
junos02 ansible_host=172.16.1.2

[junos:vars]
ansible_network_os=junos
ansible_connection=netconf

[ios]
ios01 ansible_host=172.16.2.1
ios02 ansible_host=172.16.2.22

[ios:vars]
ansible_network_os=ios
ansible_connection=network_cli

ansible-inventory コマンドの --graph オプションでは、グループとホストの関係が以下のように表示さます。

$ ansible-inventory -i inventory02.ini --graph
@all:
  |--@router:
  |  |--@ios:
  |  |  |--ios01
  |  |  |--ios02
  |  |--@junos:
  |  |  |--junos01
  |  |  |--junos02
  |--@ungrouped:
  |  |--web01

YAML の場合

YAML で書く場合は以下のようになります。

all:
  hosts:  # グループに所属しないホストの情報(ungroupedグループ所属と同義)
    web01: # ホスト web01
      ansible_host: 172.16.0.1          # ホスト変数
  children:
    router: # router グループ
      children:
        junos:  # junos グループ(routerの子グループ)
          hosts: # junos グループのホスト
            junos01:
              ansible_host: 172.16.1.1  # ホスト変数
            junos02:
              ansible_host: 172.16.1.2  # ホスト変数
          vars: # junos グループの変数
            ansible_network_os: junos
            ansible_connection: netconf
        ios:  # ios グループ(routerの子グループ)
          hosts:  # ios グループのホスト
            ios01:
              ansible_host: 172.16.2.1  # ホスト変数
            ios02:
              ansible_host: 172.16.2.2  # ホスト変数
          vars:  # ios グループの変数
            ansible_network_os: ios
            ansible_connection: network_cli

前述の簡単な例に加えて、以下のような特徴があります。

  • どのグループに所属しない(ungrouped)ホストは、all 配下の hosts に定義する
  • router グループの配下に子グループ junosios を定義する


■ 補足

注意点: 型判定が微妙に異なる

変数の値の型判定が ini と YAML で異なります。

例えば、ini 内の True は真偽値と判定されますが、 true も文字列です。一方で、YAML 内では Truetrue も真偽値です。パース処理に違いによるものです。

どちらが良いか

Ansible としては、YAML のイベントリファイルのほうが新しい機能です。 既存の ini のイベントリファルがあるのであれば、無理に YAML に移行する必要も無いと思います。Playbook も インベントリも YAML に統一したい、という事情がある場合は YAML に移行することを検討されるのがよいのではないでしょうか。

また、イベントリファル内で、複雑な変数(リストやディクショナリなど)を定義したい場合は YAML の方が適しています。

既存の ini のイベントリファイルを YAML にした場合の雰囲気を掴むには

既存の ini のイベントリファルがあって、それを YMAL で書く場合の雰囲気を掴むには以下のコマンドを利用できます。

ansible-inventory -i インベントリファイル名 --list --yaml

例えば以下の ini のイベントリファルがあったとします。

[junos]
junos01 ansible_host=172.16.1.1
junos02 ansible_host=172.16.1.2

[junos:vars]
ansible_network_os=junos
ansible_connection=netconf

出力結果は以下のようになります。

all:
  children:
    junos:
      hosts:
        junos01:
          ansible_connection: netconf
          ansible_host: 172.16.1.1
          ansible_network_os: junos
        junos02:
          ansible_connection: netconf
          ansible_host: 172.16.1.2
          ansible_network_os: junos
    ungrouped: {}

厳密には、インベントリファイルの形式変換用のコマンドではなく、インベントリファイルを解析して、各ホストにどのような変数が適用されるかなどを確認するコマンドです。例えばグループ変数は、実際に所属するホストの変数に展開されて表示されます。

ini と YAML 間の一致を確認する方法

ini から YAML に以降した場合、正しく書き換えできたかが気になると思います。その場合は、以下の2コマンドの出力結果が同じであることを確認します。

  • ansible-inventory -i iniのインベントリファイル名 --list
  • ansible-inventory -i YAMLのインベントリファイル名 --list

お好みで --yaml オプションを加えても大丈夫です。


■ まとめ

YAML 形式のインベントリファイルの書き方をご紹介しました。必要に応じて使い分けるときの参考にしていただければ幸いです。

参考

docs.ansible.com

qiita.com

[Ansible] 環境変数を取得する ansible_env.hoge と lookup("env", "hoge") の違い

■ はじめに

Ansible には 環境変数を取得するための方法として、ansible_env 配下の変数を参照する方法と、 lookup("env", "hoge") のように loopkup プラグインを利用する方法があります。これらは性質が大きく異なります。

この記事では簡単な例で動作を確認します。

■ 違いは?

ansible_env.hoge lookup("env", "hoge")
取得場所 リモートホスト(ターゲット) ローカルホスト(ansible-playbookコマンド実行ホスト)
取得タイミング gather_facts: yes (デフォルト)などによる setup 実行時 参照時(のはず)
取得可能な環境変数 setup モジュールに依存 すべて

■ 動作確認

環境

Ansible 2.7.8

Playbook

- hosts: linux  # taget1 、target2 定義済みグループ
  gather_facts: yes   # デフォルトはyes。no の場合は ansible_env 自体が定義されない

  tasks:  
    - name: debug lookup("env", "PWD") 
      debug:
        var: lookup("env", "PWD") 

    - name: debug ansible_env.PWD
      debug:
        var: ansible_env.PWD

    - name: debug ansible_env
      debug:
        var: ansible_env       # ansible_env 全体を表示

実行

$ echo $PWD # ローカルホスト上の 環境変数 `PWD` をあらかじめ確認。
/vagrant/blog/env
$ ansible-playbook -i inventory env.yml    # Playbook 実行

PLAY [linux] *************************************************************************************

TASK [Gathering Facts] ***************************************************************************
ok: [target2]
ok: [target1]

TASK [debug lookup("env", "PWD")] ****************************************************************
ok: [target1] => {
    "lookup(\"env\", \"PWD\")": "/vagrant/blog/env"
}
ok: [target2] => {
    "lookup(\"env\", \"PWD\")": "/vagrant/blog/env"
}

TASK [debug ansible_env.PWD] *********************************************************************
ok: [target1] => {
    "ansible_env.PWD": "/home/vagrant"
}
ok: [target2] => {
    "ansible_env.PWD": "/home/testuser"
}

TASK [debug ansible_env] *************************************************************************
ok: [target1] => {
    "ansible_env": {
        "HOME": "/home/vagrant", 
        "LANG": "C", 
        "LC_ALL": "C", 
        "LC_MESSAGES": "C", 
        "LESSOPEN": "||/usr/bin/lesspipe.sh %s", 
        "LOGNAME": "vagrant", 
        "LS_COLORS": "rs=0:di=38;(...略...)", 
        "MAIL": "/var/mail/vagrant", 
        "PATH": "/usr/local/bin:/usr/bin", 
        "PWD": "/home/vagrant", 
        "SELINUX_LEVEL_REQUESTED": "", 
        "SELINUX_ROLE_REQUESTED": "", 
        "SELINUX_USE_CURRENT_RANGE": "", 
        "SHELL": "/bin/bash", 
        "SHLVL": "2", 
        "SSH_CLIENT": "172.16.0.9 53026 22", 
        "SSH_CONNECTION": "172.16.0.9 53026 172.16.0.11 22", 
        "SSH_TTY": "/dev/pts/5", 
        "TERM": "xterm-256color", 
        "USER": "vagrant", 
        "XDG_RUNTIME_DIR": "/run/user/1000", 
        "XDG_SESSION_ID": "18", 
        "_": "/usr/bin/python"
    }
}
ok: [target2] => {
    "ansible_env": {
        "HOME": "/home/testuser", 
        "LANG": "ja_JP.UTF-8", 
        "LESSOPEN": "||/usr/bin/lesspipe.sh %s", 
        "LOGNAME": "testuser", 
        "LS_COLORS": "rs=0:di=38;(...略...)", 
        "MAIL": "/var/mail/testuser", 
        "PATH": "/usr/local/bin:/usr/bin", 
        "PWD": "/home/testuser", 
        "SELINUX_LEVEL_REQUESTED": "", 
        "SELINUX_ROLE_REQUESTED": "", 
        "SELINUX_USE_CURRENT_RANGE": "", 
        "SHELL": "/bin/bash", 
        "SHLVL": "2", 
        "SSH_CLIENT": "172.16.0.9 49312 22", 
        "SSH_CONNECTION": "172.16.0.9 49312 172.16.0.12 22", 
        "SSH_TTY": "/dev/pts/1", 
        "TERM": "xterm-256color", 
        "USER": "testuser", 
        "XDG_RUNTIME_DIR": "/run/user/1001", 
        "XDG_SESSION_ID": "20", 
        "_": "/usr/bin/python"
    }
}

PLAY RECAP ***************************************************************************************
target1                    : ok=4    changed=0    unreachable=0    failed=0   
target2                    : ok=4    changed=0    unreachable=0    failed=0 

確認できたこと

上記の Playbook 実行結果から以下のことが分かります。

  • lookup("env", "PWD") の値は、target1target2 向けのいずれのタスクでも /vagrant/blog/env になっています。
    • これらはローカルホスト上の環境変数 PWD の値です。
  • ansible_env.PWD の値は、target1 では /home/vagranttarget2 では /home/testuser となっています。これは各ターゲットに異なるユーザーでログインしたため、ログイン先での PWD も異なっている、という状態です。リモートホストPWD である /vagrant/blog/env とも異なっています。
  • ansible_env 配下には、すべてのローカル環境変数が入るわけではない
    • たとえば、HOSTNAME がない

■ まとめ

環境変数を取得する方法である、ansible_env.hogelookup("env", "hoge") の違いについて確認しました。 主な違いは、リモートのものなのか、ローカルのものなのかでした、

参考

docs.ansible.com

docs.ansible.com

[Ansible] Jinja2 でループインデックス(index/index0)などの特殊変数を利用する

■ はじめに

Ansible というより、Jinja2 の機能ですが for によるループの中で、今何番目のループであるかを示すループインデックスなどの特殊な変数を利用できます。

変数名
loop.index 1 から始まるインデックス
loop.index0 0 から始まるインデックス
loop.irevindex 後ろから数えるインデックス(5要素ある場合は5から)
loop.first 最初のループであれば True、それ以外は False
loop.last 最後のループであれば True、それ以外は False

この記事では簡単な例で動作を確認します。

[2019/08/20 追記] Ansible 2.8 で Ansible の「Extended loop variables」という機能として実装されました。

動作確認環境

  • Ansible 2.7.8
  • CentOS 7.6 (Ansible 側、管理対象ホスト側とも)


■ 準備

Jiinja2 テンプレート

  • test.j2
{% for u in users %}
# {{ u }} 
- index: {{ loop.index }}
- index0: {{ loop.index0 }}
- revindex: {{ loop.revindex}}
- first: {{ loop.first}}
- last: {{ loop.last}}

{% endfor %}

Playbook

テンプレート test.j2 の内容をもとにして test.txt を生成します。ここでは、変数はvars ディレクティブで指定したものが利用されます。

  • test.yml
- hosts: localhost
  gather_facts: no

  tasks:
    - name: loop test
      template:
        src: test.j2
        dest: test.txt

  vars:
    users:
      - kingyo
      - koi
      - funa
      - tanago
      - oikawa

■ 実行

$ ansible-playbook -i inventory test.yml 

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

TASK [loop test] *********************************************************
ok: [localhost]

PLAY RECAP *************************************************************
localhost                  : ok=1    changed=0    unreachable=0    failed=0   
  • test.txt

生成したファイルの内容です。各種変数の内容が確認できます。

# kingyo 
- index: 1
- index0: 0
- revindex: 5
- first: True
- last: False

# koi 
- index: 2
- index0: 1
- revindex: 4
- first: False
- last: False

# funa 
- index: 3
- index0: 2
- revindex: 3
- first: False
- last: False

# tanago 
- index: 4
- index0: 3
- revindex: 2
- first: False
- last: False

# oikawa 
- index: 5
- index0: 4
- revindex: 1
- first: False
- last: True

■ まとめ

Jinja2 テンプレートのループの中で、ループインデックスなどの特殊な変数を確認しました。 その他の loop.* 変数については Jinja2 のドキュメントを参照してください。

jinja.pocoo.org

なお、これらの変数は Ansilbe 徹底入門をきっかけに知りました。

Ansible徹底入門 クラウド時代の新しい構成管理の実現

Ansible徹底入門 クラウド時代の新しい構成管理の実現