Merging variables in Ansible with roles
I'm configuring my environment via the following:
inventory.yml
all:
children:
production:
1.2.3.4
staging:
1.2.3.5
In group_vars/all.yml I'm setting up a hash of users which will be added in a playbook. I'd like to be able to add users specifically to group_vars/staging.yml that would be merged with the same setting in my group_vars/all.yml.
Is there a proper way to merge the hash or declare an inheritance in this case?
See DEFAULT_HASH_BEHAVIOUR. Quoting
"This setting controls how variables merge in Ansible. By default, Ansible will override variables in specific precedence orders, as described in Variables. When a variable of higher precedence wins, it will replace the other value. Some users prefer that variables that are hashes (aka ‘dictionaries’ in Python terms) are merged. This setting is called ‘merge’. ..."
For example, given the inventory and group_vars
shell> cat hosts
all:
children:
production:
hosts:
1.2.3.4
staging:
hosts:
1.2.3.5
shell> cat group_vars/all.yml
users:
admin:
shell: /bin/bash
ansible:
shell: /bin/sh
shell> cat group_vars/production/users.yml
users:
dealer:
shell: /usr/sbin/nologin
shell> cat group_vars/staging/users.yml
users:
tester:
shell: /bin/bash
The playbook
shell> cat pb.yml
- hosts: all
tasks:
- debug:
var: users
by default overrides the dictionaries. Gives (abridged)
shell> ansible-playbook pb.yml
TASK [debug] ****
ok: [1.2.3.4] =>
users:
dealer:
shell: /usr/sbin/nologin
ok: [1.2.3.5] =>
users:
tester:
shell: /bin/bash
When ANSIBLE_HASH_BEHAVIOUR is set to merge the dictionaries the playbook gives (abridged)
shell> ANSIBLE_HASH_BEHAVIOUR=merge ansible-playbook pb.yml
TASK [debug] ****
ok: [1.2.3.4] =>
users:
admin:
shell: /bin/bash
ansible:
shell: /bin/sh
dealer:
shell: /usr/sbin/nologin
ok: [1.2.3.5] =>
users:
admin:
shell: /bin/bash
ansible:
shell: /bin/sh
tester:
shell: /bin/bash
This setting will be deprecated in 2.13.
Quoting the Deprecated detail
"This feature is fragile and not portable, leading to continual confusion and misuse"
Quoting the Deprecated alternatives
"the combine filter explicitly"
For example, rename the common users' dictionary to users_all
shell> cat group_vars/all.yml
users_all:
admin:
shell: /bin/bash
ansible:
shell: /bin/sh
Then the filter combine merges the dictionaries
shell> cat pb.yml
- hosts: all
tasks:
- debug:
var: users_all|combine(users)
gives (abridged)
shell> ansible-playbook pb.yml
TASK [debug] ****
ok: [1.2.3.4] =>
users_all|combine(users):
admin:
shell: /bin/bash
ansible:
shell: /bin/sh
dealer:
shell: /usr/sbin/nologin
ok: [1.2.3.5] =>
users_all|combine(users):
admin:
shell: /bin/bash
ansible:
shell: /bin/sh
tester:
shell: /bin/bash