merging dictionaries in ansible
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"?
Solution 1:
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) }}"
Solution 2:
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 ... [email protected] [email protected]
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.
Solution 3:
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.
Solution 4:
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