This is part of the series "The Horrors of Ansible Complex Variables." The results below were achieved with Ansible version 2.8.0.
I was happy to have a "dictionary" format that worked in both Ansible and Jinja2 (in quotes because it's a bit more complex than that, see the previous entry in this series), but I wanted not only a list of users but also each user to contain a list of the groups they were in. Here's a demonstration:
---
# filename: groups.yaml
- name: lists
hosts: localhost
connection: local
vars:
# you don't have to align this stuff at all: I like it this way
# because it's more readable (but it's a pain to maintain when you
# add accounts)
#
# I also apologize that I didn't mention [] as the Nth way of creating
# lists in Ansible in the blog entry on lists ...
users:
- { name: giles, groups: ['readonly', 'admin'], fullname: "Giles Orr" }
- { name: dilbert, groups: ['main', 'admin'], fullname: "Dilbert" }
- { name: jenkins, groups: ['readonly'], fullname: "Butler Jenkins" }
- { name: dogbert, groups: ['main', 'trolls'], fullname: "Dogbert" }
- { name: deploy, groups: ['readonly'], fullname: "Deploy Account" }
tasks:
- name: Print users (full object)
debug:
msg: "{{ item }}"
loop: "{{ users }}"
- name: Print users
debug:
msg: "User {{ item.name }} has {{ item.groups }} perms"
loop: "{{ users }}"
- name: template with the dictionary
template:
src=template.j2
dest=template.txt
Once again, a (more complex) template file to go with it:
# {{ ansible_managed }}
{# vim: syntax=jinja2 sr et ts=4 sw=4 sts=4: #}
main = {% for user in users %}{% if "main" in user.groups %}{{ user.name }}, {% endif %}{% endfor %}
admin = {% for user in users %}{% if "admin" in user.groups %}{{ user.name }}, {% endif %}{% endfor %}
readonly = {% for user in users %}{% if "readonly" in user.groups %}{{ user.name }}, {% endif %}{% endfor %}
trolls = {% for user in users %}{% if "trolls" in user.groups %}{{ user.name }}, {% endif %}{% endfor %}
And now for your /etc/passwd file (needs more details):
{% for user in users %}
{{ user.name }}::::{{ user.fullname}}::
{% endfor %}
{# make a list of all groups (including users, who are usually their own
group on Linux).
The dashes inside the percent signs in the statements are to control
the whitespace: we would otherwise have a bunch of empty lines.
#}
{%- set groups = [] -%}
{%- for user in users -%}
{{ groups.append( user.name ) }}
{%- for group in user.groups -%}
{%- if group not in groups -%}
{{ groups.append( group ) }}
{%- endif -%}
{%- endfor -%}
{%- endfor -%}
All groups: {{ groups|join(',') }}
And for /etc/group (again, more details needed):
{% for group in groups %}
{{ group }}:x::{% for user in users %}{% if group in user.groups %}{{ user.name }},{% endif %}{% endfor %}
{% endfor %}
This includes a couple different ways of handling these usernames depending on the output format you want.
When run, it looks like this:
$ ansible-playbook groups.yml
[WARNING]: provided hosts list is empty, only localhost is available. Note that the
implicit localhost does not match 'all'
PLAY [lists] *******************************************************************************
TASK [Gathering Facts] *********************************************************************
ok: [localhost]
TASK [Print users (full object)] ***********************************************************
ok: [localhost] => (item={'name': 'giles', 'groups': ['readonly', 'admin'], 'fullname': 'Giles Orr'}) => {
"msg": {
"fullname": "Giles Orr",
"groups": [
"readonly",
"admin"
],
"name": "giles"
}
}
ok: [localhost] => (item={'name': 'dilbert', 'groups': ['main', 'admin'], 'fullname': 'Dilbert'}) => {
"msg": {
"fullname": "Dilbert",
"groups": [
"main",
"admin"
],
"name": "dilbert"
}
}
ok: [localhost] => (item={'name': 'jenkins', 'groups': ['readonly'], 'fullname': 'Butler Jenkins'}) => {
"msg": {
"fullname": "Butler Jenkins",
"groups": [
"readonly"
],
"name": "jenkins"
}
}
ok: [localhost] => (item={'name': 'dogbert', 'groups': ['main', 'trolls'], 'fullname': 'Dogbert'}) => {
"msg": {
"fullname": "Dogbert",
"groups": [
"main",
"trolls"
],
"name": "dogbert"
}
}
ok: [localhost] => (item={'name': 'deploy', 'groups': ['readonly'], 'fullname': 'Deploy Account'}) => {
"msg": {
"fullname": "Deploy Account",
"groups": [
"readonly"
],
"name": "deploy"
}
}
TASK [Print users] *************************************************************************
ok: [localhost] => (item={'name': 'giles', 'groups': ['readonly', 'admin'], 'fullname': 'Giles Orr'}) => {
"msg": "User giles has ['readonly', 'admin'] perms"
}
ok: [localhost] => (item={'name': 'dilbert', 'groups': ['main', 'admin'], 'fullname': 'Dilbert'}) => {
"msg": "User dilbert has ['main', 'admin'] perms"
}
ok: [localhost] => (item={'name': 'jenkins', 'groups': ['readonly'], 'fullname': 'Butler Jenkins'}) => {
"msg": "User jenkins has ['readonly'] perms"
}
ok: [localhost] => (item={'name': 'dogbert', 'groups': ['main', 'trolls'], 'fullname': 'Dogbert'}) => {
"msg": "User dogbert has ['main', 'trolls'] perms"
}
ok: [localhost] => (item={'name': 'deploy', 'groups': ['readonly'], 'fullname': 'Deploy Account'}) => {
"msg": "User deploy has ['readonly'] perms"
}
TASK [template with the dictionary] ********************************************************
changed: [localhost]
PLAY RECAP *********************************************************************************
localhost : ok=4 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
And the output of the template looks like this:
# Ansible managed main = dilbert, dogbert, admin = giles, dilbert, readonly = giles, jenkins, deploy, trolls = dogbert, And now for your /etc/passwd file (needs more details): giles::::Giles Orr:: dilbert::::Dilbert:: jenkins::::Butler Jenkins:: dogbert::::Dogbert:: deploy::::Deploy Account:: All groups: giles,readonly,admin,dilbert,main,jenkins,dogbert,trolls,deploy And for /etc/group (again, more details needed): giles:x:: readonly:x::giles,jenkins,deploy, admin:x::giles,dilbert, dilbert:x:: main:x::dilbert,dogbert, jenkins:x:: dogbert:x:: trolls:x::dogbert, deploy:x::
Bibliography
- https://stackoverflow.com/questions/28294816/remove-the-empty-lines-left-by-jinja2-variable-definitions - Jinja2 whitespace control