1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

Ansibleで同じディクショナリーに属する変数をset_factで複数回上書きしても、反映されるのは一番最後の上書きだけだった

Last updated at Posted at 2020-08-04

概要

たまにCI/CDでやりたくなるAnsibleのvars上書き芸。

こんな感じの優先順位で上書きされますよね。(下に行くほど優先順位が高い)

command line values (eg “-u user”)
role defaults [1]
inventory file or script group vars [2]
inventory group_vars/all [3]
playbook group_vars/all [3]
inventory group_vars/* [3]
playbook group_vars/* [3]
inventory file or script host vars [2]
inventory host_vars/* [3]
playbook host_vars/* [3]
host facts / cached set_facts [4]
play vars
play vars_prompt
play vars_files
role vars (defined in role/vars/main.yml)
block vars (only for tasks in block)
task vars (only for the task)
include_vars
set_facts / registered vars
role (and include_role) params
include params
extra vars (always win precedence)

参考: Using Variables — Ansible Documentation



例えば… ansible.cfghash_behaviour=merge としておけば…

roles/hoge_role/defaults/main.yml とかで下記みたいに変数を定義して…

dict:
  a: "aaa"
  b: "bbb"
  c: "ccc"

hosts/host_group_a/group_vars/all.yml で下記のように定義すれば。

dict:
  a: "override"

こんな結果が得られます。

dict:
  a: "override"
  b: "bbb"
  c: "ccc"

このノリで set_fact を触ったらハマった。

やりたかったこと

roles/hoge_role/defaults/main.yml とかで下記みたいに変数を定義。

dict:
  a: "aaa"
  b: "bbb"
  c: "ccc"

hosts/host_group_a/group_vars/all.yml で下記のように定義した上で…

dict:
  a: "override"

更に bc についても上書きする。この上書きは特定の --extra-vars で指定した変数から値を持ってくる。
なおかつこれらの変数は指定されないこともある…

などというトリッキーなことをしたいとする。

ハマった方法

- name: "Load B"
  set_fact:
    dict:
      b: "{{ B }}"
  when: B is defined

- name: "Load C"
  set_fact:
    dict:
      c: "{{ C }}"
  when: C is defined

とかってして、 --extra-vars "B=hoge" とか --extra-vars "C=fuga" とか --extra-vars "B=hoge C=fuga" としたかった。

で、 --extra-vars "B=hoge C=fuga" の時にうまく行かない。こんな感じになるわけですね。

dict:
  a: "override"
  b: "bbb"
  c: "fuga"

本当に必要としていた結果はこれですよね。

dict:
  a: "override"
  b: "hoge"
  c: "fuga"

どうやら、set_factのmargeには癖があるらしい。

どうするのが良いんだろうね

こんな感じ?釈然としない。

- name: "Load B"
  set_fact:
    dict:
      b: "{{ B }}"
  when:
    - B is defined
    - not C is defined

- name: "Load C"
  set_fact:
    dict:
      c: "{{ C }}"
  when:
    - not B is defined
    - C is defined

- name: "Load B and C"
  set_fact:
    dict:
      b: "{{ B }}"
      c: "{{ C }}"
  when:
    - B is defined
    - C is defined

追記: 寄せられたもう少しスマートな方法

@hiroyuki_onodera さんよりコメント頂いた方法。

- name: Load B,C
  set_fact:
    dict:
      b: "{{ B|default(omit) }}"
      c: "{{ C|default(omit) }}"

なるほど。 default フィルターってのがあるんですね。こちょこちょいじらない場合はこれがスマートそう。

フィルター — Ansible Documentation

まとめ

あんまりトリッキーなことはやめておきましょう。
ドキュメント流し読みしかしてないけど、どこかにこの挙動って書いてあったっけ…?

1
1
3

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?