# Reading in extra files, as or into dicts?

**URL:** <https://forum.ansible.com/t/reading-in-extra-files-as-or-into-dicts/36536>\
**Category:** Ansible Project\
**Created:** [April 16, 2022, 10:13am UTC](https://forum.ansible.com/t/reading-in-extra-files-as-or-into-dicts/36536 "2022-04-16T10:13:28Z")\
**Posts on this page:** 5\
**Page:** 1

<div class="post-metadata">

**Author:** ![Richard\_Hector](https://avatars.discourse-cdn.com/v4/letter/r/db5fbb/32.png) [@Richard\_Hector](https://forum.ansible.com/u/Richard_Hector)\
**Post date:** [April 16, 2022, 10:13am UTC](https://forum.ansible.com/t/reading-in-extra-files-as-or-into-dicts/36536/1 "2022-04-16T10:13:28Z")

</div>

Hi all,

I have created a directory 'users' alongside my inventory. It has a directory 'user\_vars', intended to be used like host\_vars, but for users, obviously.

In there, I have files like this:

---

<div class="post-metadata">

**Author:** ![Richard\_Hector](https://avatars.discourse-cdn.com/v4/letter/r/db5fbb/32.png) [@Richard\_Hector](https://forum.ansible.com/u/Richard_Hector)\
**Post date:** [April 16, 2022, 11:52am UTC](https://forum.ansible.com/t/reading-in-extra-files-as-or-into-dicts/36536/2 "2022-04-16T11:52:14Z")

</div>

I've got this, but it looks horrible:

---

<div class="post-metadata">

**Author:** ![Michael\_Stroder](https://avatars.discourse-cdn.com/v4/letter/m/7ea924/32.png) [@Michael\_Stroder](https://forum.ansible.com/u/Michael_Stroder)\
**Post date:** [April 16, 2022, 12:55pm UTC](https://forum.ansible.com/t/reading-in-extra-files-as-or-into-dicts/36536/3 "2022-04-16T12:55:50Z")

</div>

> > I have created a directory 'users' alongside my inventory. It has a directory 'user\_vars', intended to be used like host\_vars, but for users, obviously.
> 
> I've got this, but it looks horrible:
> 
> ==================  
> - name: set up user dicts  
> set\_fact:

You could avoid this by just using regular group\_vars/.

> Are there ways to make this cleaner?

Probably not the answer you're looking for: While I use ansible for many things I'd always prefer a decent user management system over hacking this into ansible playbooks. Maintaining users, groups and maybe sudoers in a database gives you required features like enforcing unique constraints (username, POSIX-UID/-GID), data references, audit reports etc. There are various ready-to-use systems (most times based on some LDAP server) to choose from.

Ciao, Michael.

---

<div class="post-metadata">

**Author:** ![utoddl](https://sea1.discourse-cdn.com/ansible/user_avatar/forum.ansible.com/utoddl/32/249_2.png) [@utoddl](https://forum.ansible.com/u/utoddl)\
**Post date:** [April 16, 2022, 5:22pm UTC](https://forum.ansible.com/t/reading-in-extra-files-as-or-into-dicts/36536/4 "2022-04-16T17:22:48Z")

</div>

> Any suggestions?

Questions more than suggestions, but you can take them as such anyway.

1. How many users are we talking about? If the user data is smaller than the code it takes to manipulate it, maybe it’d be better to make the data more complex and the code simpler.

2. Are these files the canonical source of truth, or are they downstream? If downstream, perhaps it would be better to pull the data from upstream as needed. If these files are canonical truth, then either restructure them in a way that supports cleaner access, or consider creating an upstream data source. The latter would only make sense if the answer to #1 was large enough to justify it.

If it were me, and assuming a fairly small number of users and/or relatively static data (i.e. I’m going to maintain this by hand for the foreseeable future), I’d put all these user data into defaults/main.yml of a custom “my\_users” role, probably in a single dict keyed by ID, or maybe a list. Make everything that might have multiple values for any user a list - ssh keys for example - so even if a user has only one (or none) you access that data the same way for everybody.

The fact that the expressions you’re having to use to access the data currently are unwieldy indicates the initial data structures are a problem. Having user data split into multiple files may seem like a simplification, but I think it’s just the opposite.

Good luck!

---

<div class="post-metadata">

**Author:** ![vbotka](https://sea1.discourse-cdn.com/ansible/user_avatar/forum.ansible.com/vbotka/32/359_2.png) [@vbotka](https://forum.ansible.com/u/vbotka)\
**Post date:** [April 16, 2022, 7:31pm UTC](https://forum.ansible.com/t/reading-in-extra-files-as-or-into-dicts/36536/5 "2022-04-16T19:31:37Z")

</div>

For example, given the tree

> tree inventory/

inventory/  
├── group\_vars  
│ └── all  
├── hosts  
└── users  
&nbsp;&nbsp;&nbsp;&nbsp;├── public\_keys  
&nbsp;&nbsp;&nbsp;&nbsp;│ ├── alice@bar  
&nbsp;&nbsp;&nbsp;&nbsp;│ ├── alice@foo  
&nbsp;&nbsp;&nbsp;&nbsp;│ ├── richard@bar  
&nbsp;&nbsp;&nbsp;&nbsp;│ └── richard@foo  
&nbsp;&nbsp;&nbsp;&nbsp;└── user\_vars  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;├── alice  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;└── richard

and the files for testing

> cat inventory/users/user\_vars/alice

name: alice  
gecos: 'Alice Beatrice,'  
shell: '/bin/bash'  
ssh\_keys:  
&nbsp;&nbsp;&nbsp;- alice@foo  
&nbsp;&nbsp;&nbsp;- alice@bar

> cat inventory/users/user\_vars/richard

name: richard  
gecos: 'Richard Hector,'  
shell: '/bin/bash'  
ssh\_keys:  
&nbsp;&nbsp;&nbsp;- richard@foo  
&nbsp;&nbsp;&nbsp;- richard@bar

> cat inventory/users/public\_keys/

alice@bar alice@foo richard@bar richard@foo

> cat inventory/users/public\_keys/alice@bar

ssh-rsa djflskdjflsdkjflsdjflkdjl alice@bar

> cat inventory/users/public\_keys/alice@foo

ssh-rsa djflskdjflsdkjflsdjflkdjl alice@foo

> cat inventory/users/public\_keys/richard@bar

ssh-rsa djflskdjflsdkjflsdjflkdjl richard@bar

> cat inventory/users/public\_keys/richard@foo

ssh-rsa djflskdjflsdkjflsdjflkdjl richard@foo

\* Create variables

&nbsp;&nbsp;inventory\_dir: inventory  
&nbsp;&nbsp;dyn\_vars\_list: [user\_vars, public\_keys]

\* Get the names of the files

&nbsp;&nbsp;&nbsp;&nbsp;- set\_fact:  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;keys: "{{ keys|d({})|  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;combine({item: files}) }}"  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;loop: "{{ dyn\_vars\_list }}"  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;vars:  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;fileglob: "{{ inventory\_dir }}/users/{{ item }}/\*"  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;files: "{{ query('fileglob', fileglob)|  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;map('basename')|list }}"

gives

&nbsp;&nbsp;keys:  
&nbsp;&nbsp;&nbsp;&nbsp;public\_keys:  
&nbsp;&nbsp;&nbsp;&nbsp;- richard@bar  
&nbsp;&nbsp;&nbsp;&nbsp;- alice@bar  
&nbsp;&nbsp;&nbsp;&nbsp;- alice@foo  
&nbsp;&nbsp;&nbsp;&nbsp;- richard@foo  
&nbsp;&nbsp;&nbsp;&nbsp;user\_vars:  
&nbsp;&nbsp;&nbsp;&nbsp;- alice  
&nbsp;&nbsp;&nbsp;&nbsp;- richard

\* Get the content of the files

&nbsp;&nbsp;&nbsp;&nbsp;- set\_fact:  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;values: "{{ values|d() +  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;[{'var': item.0.key,  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;'key': item.1,  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;'value': value}] }}"  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;with\_subelements:  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;- "{{ keys|dict2items }}"  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;- value  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;vars:  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;path: "{{ inventory\_dir }}/users/{{ item.0.key }}/{{ item.1 }}"  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;value: "{{ lookup('file', path)|from\_yaml }}"

gives

&nbsp;&nbsp;values:  
&nbsp;&nbsp;- key: alice  
&nbsp;&nbsp;&nbsp;&nbsp;value:  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;gecos: Alice Beatrice,  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;name: alice  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;shell: /bin/bash  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ssh\_keys:  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;- alice@foo  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;- alice@bar  
&nbsp;&nbsp;&nbsp;&nbsp;var: user\_vars  
&nbsp;&nbsp;- key: richard  
&nbsp;&nbsp;&nbsp;&nbsp;value:  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;gecos: Richard Hector,  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;name: richard  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;shell: /bin/bash  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ssh\_keys:  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;- richard@foo  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;- richard@bar  
&nbsp;&nbsp;&nbsp;&nbsp;var: user\_vars  
&nbsp;&nbsp;- key: richard@bar  
&nbsp;&nbsp;&nbsp;&nbsp;value: ssh-rsa dkjfslkdjflskdjflsdkjflsdjflkdjl richard@bar  
&nbsp;&nbsp;&nbsp;&nbsp;var: public\_keys  
&nbsp;&nbsp;- key: alice@bar  
&nbsp;&nbsp;&nbsp;&nbsp;value: ssh-rsa dkjfslkdjflskdjflsdkjflsdjflkdjl alice@bar  
&nbsp;&nbsp;&nbsp;&nbsp;var: public\_keys  
&nbsp;&nbsp;- key: alice@foo  
&nbsp;&nbsp;&nbsp;&nbsp;value: ssh-rsa dkjfslkdjflskdjflsdkjflsdjflkdjl alice@foo  
&nbsp;&nbsp;&nbsp;&nbsp;var: public\_keys  
&nbsp;&nbsp;- key: richard@foo  
&nbsp;&nbsp;&nbsp;&nbsp;value: ssh-rsa dkjfslkdjflskdjflsdkjflsdjflkdjl richard@foo  
&nbsp;&nbsp;&nbsp;&nbsp;var: public\_keys

\* Group the values by 'var' and create the dictionary

&nbsp;&nbsp;&nbsp;&nbsp;- set\_fact:  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;dyn\_vars: "{{ dyn\_vars|d({})|  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;combine({item.0: item.1|  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;items2dict(key\_name='key',  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;value\_name='value')}) }}"  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;loop: "{{ values|groupby('var') }}"

gives

&nbsp;&nbsp;dyn\_vars:  
&nbsp;&nbsp;&nbsp;&nbsp;public\_keys:  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;alice@bar: ssh-rsa djflskdjflsdkjflsdjflkdjl alice@bar  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;alice@foo: ssh-rsa djflskdjflsdkjflsdjflkdjl alice@foo  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;richard@bar: ssh-rsa djflskdjflsdkjflsdjflkdjl richard@bar  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;richard@foo: ssh-rsa djflskdjflsdkjflsdjflkdjl richard@foo  
&nbsp;&nbsp;&nbsp;&nbsp;user\_vars:  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;alice:  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;gecos: Alice Beatrice,  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;name: alice  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;shell: /bin/bash  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ssh\_keys:  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;- alice@foo  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;- alice@bar  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;richard:  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;gecos: Richard Hector,  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;name: richard  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;shell: /bin/bash  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ssh\_keys:  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;- richard@foo  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;- richard@bar

The usage of the dictionary should be trivial.
