Ansible uses Jinja2 for the variable Filter.
Do you sometimes want to deep merge variables including dictionaries cleanly? Today, I will introduce two ways to realize deep merge of variables in Ansible. What's different is that the method is different for Ansible 2.0 and above and below.
A new ** combine filter ** has been implemented since Ansible 2.0. http://docs.ansible.com/ansible/playbooks_filters.html#combining-hashes-dictionaries
test.yml
- hosts: localhost
gather_facts: no
vars:
dict:
foo:
bar: 1
dict2:
foo:
baz: 2
qux: 2
# combining hashes/dictionaries (new in version 2.0)
dict_combine: "{{ dict | combine(dict2, recursive=True) }}"
tasks:
- debug:
var: dict_combine
If you do the above, you will get the following results.
$ ansible-playbook -i localhost, test.yml
PLAY [localhost] ***************************************************************
TASK [debug] *******************************************************************
ok: [localhost] => {
"dict_combine": {
"foo": {
"bar": 1,
"baz": 2
},
"qux": 2
}
}
It's very convenient. The contents of dict-> foo are properly merged. By the way, omitting "recursive" is just an update merge.
recursive=false
$ ansible-playbook -i localhost, test.yml
PLAY [localhost] ***************************************************************
TASK [debug] *******************************************************************
ok: [localhost] => {
"dict_combine": {
"foo": {
"baz": 2
},
"qux": 2
}
}
By the way, the function equivalent to combine that omits recursive could be realized as follows by using update () even in the version before Ansible 2.0.
test.yml
- hosts: localhost
gather_facts: no
vars:
dict:
foo:
bar: 1
dict2:
foo:
baz: 2
qux: 2
# **[Note]**
# jinja2 'do' tag need expression-statement extension
# please set below to [default] section in ansible.cfg
# jinja2_extensions=jinja2.ext.do
dict_update: |
{% do dict.update(dict2) %}
{{ dict }}
tasks:
- debug:
var: dict_update
If you have to use Ansible 1.X for various reasons, it is a little inconvenient because you cannot use combine. Of course, you can import the source part of combine implemented in 2.0, but since it's a big deal, I'd like to create my own Filter plugin. http://docs.ansible.com/ansible/developing_plugins.html#filter-plugins
To use plugin, put the path (filter_plugins =) in ansible.cfg or put the custom file directly in the plugin directory of ansible itself and it will be read when ansible is executed.
/path-to/ansible/filter_plugins/dict_merge.py
from copy import deepcopy
def dict_merge(a, b):
if not isinstance(b, dict):
return b
result = deepcopy(a)
for k, v in b.iteritems():
if k in result and isinstance(result[k], dict):
result[k] = dict_merge(result[k], v)
else:
result[k] = deepcopy(v)
return result
class FilterModule(object):
def filters(self):
return {'dict_merge': dict_merge}
This completes the plug-in creation. It's easier than you think. Let's test it.
test.yml
- hosts: localhost
gather_facts: no
vars:
dict:
foo:
bar: 1
dict2:
foo:
baz: 2
qux: 2
# custom filter plugin
dict_merged: "{{ dict | dict_merge(dict2) }}"
tasks:
- debug:
var: dict_merged
The execution result is as follows.
$ ansible-playbook -i localhost, test.yml
PLAY [localhost] ***************************************************************
TASK [debug] *******************************************************************
ok: [localhost] => {
"dict_merged": {
"foo": {
"bar": 1,
"baz": 2
},
"qux": 2
}
}
I was able to get the same result as combine. Of course, it also merges deeper dicts.
In addition to the Filter plugin, Ansible has several plugins that you can create your own. http://docs.ansible.com/ansible/developing_plugins.html
Lookup and callbacks plugins are relatively informative and have information on people doing various things.
Let's enjoy your Ansible life!