I'm currently building a role for installing PHP using ansible, and I'm having some difficulty merging dictionaries. I've tried several ways to do so, but I can't get it to work like I want it to:
# A vars file:
my_default_values:
key = value
my_values:
my_key = my_value
# In a playbook, I create a task to attempt merging the
# two dictionaries (which doesn't work):
- debug: msg="{{ item.key }} = {{ item.value }}"
with_dict: my_default_values + my_values
# I have also tried:
- debug: msg="{{ item.key }} = {{ item.value }}"
with_dict: my_default_values|union(my_values)
# I have /some/ success with using j2's update,
# but you can't use j2 syntax in "with_dict", it appears.
# This works:
- debug: msg="{{ my_default_values.update(my_values) }}"
# But this doesn't:
- debug: msg="{{ item.key }} = {{ item.value }}"
with_dict: my_default_values.update(my_values)
Is there a way to merge two dictionaries, so I can use it with "with_dict"?
In Ansible 2.0, there is a Jinja filter, combine
, for this:
- debug: msg="{{ item.key }} = {{ item.value }}"
with_dict: "{{ my_default_values | combine(my_values) }}"
If you want hash merging I would turn the hash merging feature on in ansible.
In your ansible config file turn hash merging on.
With hash_behaviour=merge you can have two var files with the same variable name:
defaults.yml:
values:
key: value
overrides.yml:
values:
my_key: my_value
In order for the two vars to be merged you will need to include both var files:
ansible-playbook some-play.yml ... -e@defaults.yml -e@overrides.yml
And you will end up with this:
TASK: [debug var=values] ********************************************************
ok: [localhost] => {
"values": {
"key": value,
"my_key": my_value
}
}
Calling update on a variable can be done in Jinja but in general it will be messy, I wouldn't do it outside of your templates and even then try to avoid it altogether.
It is now possible to use the anchor and extend features of YAML:
---
- hosts: localhost
vars:
my_default_values: &def
key: value
my_values:
<<: *def
my_key: my_value
tasks:
- debug: var=my_default_values
- debug: var=my_values
Result:
TASK [debug]
ok: [localhost] => {
"my_default_values": {
"key": "value"
}
}
TASK [debug]
ok: [localhost] => {
"my_values": {
"key": "value",
"my_key": "my_value"
}
}
I have no idea why this was not mentioned before.
If you need the merged dictionary a few times, you can set it to a new "variable":
- set_fact: _my_values="{{ my_default_values|combine(my_values) }}"
- debug: msg="{{ item.key }} = {{ item.value }}"
with_dict: _my_values
Try this role from Ansible Galaxy.
I did it some time ago for same reason. It can deep merge dictionaries from several vars files and set custom precedence of merging.
This role can work under Ansible 2.0+
>>> key = 'default key'
>>> value = 'default value'
>>> my_key = 'my key'
>>> my_value = 'my value'
>>>
>>> my_default_values = {key: value}
>>> print my_default_values
{'default key': 'default value'}
>>>
>>> my_values = {my_key: my_value}
>>> print my_values
{'my key': 'my value'}
>>>
>>> with_dict = my_default_value.copy()
>>> print with_dict
{'default key': 'default value'}
>>> with_dict.update(my_values)
>>> print with_dict
{'default key': 'default value', 'my key': 'my value'}