Importing user variables from multiple variable sources

Hi,

I want to know how I can import user account variables at multiple levels using loops/arrays and importing them all.

i.e. i have a generic module which has users defined in roles/common/tasks/main.yml as an array(or loop if more precise):

  • name: add admin users on RHEL
    user:
    name: “{{ item.username }}”
    comment: “{{ item.comment }}”
    state: present
    groups: wheel
    shell: /bin/bash
    password: “{{ item.password }}”
    with_items: “{{ users }}”
    when: ansible_facts[“os_family”] == “RedHat”
    tags: common

and the user attributes are all set as variables and the variables for these users are being picked up from roles/common/vars/main.yml

I also have user variables defined under group_vars/dev and group_vars/prd

And I also have specific users defined at host level under host_vars/server1

Now the problem is that Ansible is only picking up users from one level, whilst I want to pick up all the user variables from all levels, i.e. sysadmins, dev users & specific host users, and add them all in, not just from the most preferred variable source.

In Puppet this is achieved by using Hiera and “hiera_hash” which allows variable values to get collectively applied from all hiera levels.

How can the same be achieved with Ansible please?

Regards,
Suhail.

Has anyone come across this before? Surely it must be a common ask.

I have a similar issue and solved it by this PR https://github.com/ansible/ansible/pull/51466 . if you want to use it, just grab the source code and add it to your local library directory until it's merged. Also see this thread https://groups.google.com/forum/?utm_medium=email&utm_source=footer#!msg/ansible-project/Ssnk6exNjNs/sCKlrOp_FQAJ

With the approach above, the code would merge together all lists that match a pattern, this way you can defined as many "users" variables as you want as long as they are unique and start with say "users_"

If you don't want to copy the library or wait for the PR, then you could also just merge a set of predefined lists.

ie..
with_items: "{{ users_host|default() + users_group_l1|default() + users_group_l2|default([[) }}"

Thanks for your earlier reply Adam.

So how can I combine variables for a list of “users” defined like so:

group_vars/all

users:

  • name: alice
    comment: Alice

group_vars/group1

users:

  • name: bob
    comment: Bob

host_vars/host1

users:

  • name: charlie
    comment: Charlie

How can all these variables be combined using “loop” or “with_items”?
What if there are duplicates?
Can the “host_vars/host1” variables override and take precedence over the “group_vars/all” variables?

Regards,
Suhail.

Thanks for your earlier reply Adam.

So how can I combine variables for a list of "users" defined like so:

*group_vars/all*

users:
   - name: alice
     comment: Alice

*group_vars/group1*

users:
   - name: bob
     comment: Bob

*host_vars/host1*

users:
   - name: charlie
     comment: Charlie

How can all these variables be combined using "loop" or "with_items"?

You can't, they will be overwritten in the order listed here
https://docs.ansible.com/ansible/latest/user_guide/playbooks_variables.html#variable-precedence-where-should-i-put-a-variable

What if there are duplicates?

They are duplicates since all are call users and will be overwritten.

Can the "host_vars/host1" variables override and take precedence over the
"group_vars/all" variables?

It does.

Hi.

Thanks for reply Kai.

Default Ansible cannot solve this problem, which I find very odd as it
was a common issue that is very elegantly addressed in Pupept using
Hiera.

However.... a solution has been found!

Have a look at:

http://leapfrogonline.io/articles/2017-02-22-explicit-merging-of-ansible-variables/
https://github.com/leapfrogonline/ansible-merge-vars

They have solved exactly this issue and I'm testing their plugin and
so far so good. Certainly not as elegant as Puppet/Hiera but it's a
working solution.

Regards,
Suhail.

another alternative if you don’t want to use an external plugin is to predefine your variable names and then combine them…

ie…

users_global

users_group

users_host (variable you set in host_vars)

and then use the “combine” filter to combine them together .
ie…

users_global | combine(users_group, recursive=True) | combine(users_host, recursive=True)

I agree though, I am also coming from puppet and miss hiera. I may still consider going back to using it as there appears to be support for it, but it would be nice to be fully integrated

The strategy I ended up going with for the similar problem you have is

  1. I created a “users” hash in vars/ which contains all users that can be managed
  2. then in my host & group_vars I populate a list such as “linux_users_global” or linux_users_unique_name which contains the list of users that should be managed either globally, group or at the host level
  3. then in my playbook I merge all the variables that match the pattern “linux_users.*” . (see this link)
  4. then I loop through that merged list and manage those users

for me, the above solution works quite well.