Correct way to create dynamic lists in Ansible
Solution 1
Filters will operate on lists, so the with_items is really wasteful, and the regex stuff is pretty obtuse for what you're doing. Do you really want a comma-separated string, or do you just want a list of the usernames extracted from the admin_accounts
list?
If you just want the list, why not:
set_fact:
admin_usernames: "{{ admin_accounts | map(attribute='name') | list }}"
... and if you really want the comma-separated list as a flat string, just add a join filter:
set_fact:
admin_usernames: "{{ admin_accounts | map(attribute='name') | join(', ') }}"
If your ultimate target is a template, though, I'd suggest doing this inside the template, since this looks pretty formatting-related as opposed to logic-related (unless you're just simplifying for Stack Overflow purposes)...
Solution 2
When there is a need to add both prefix and suffix (and making everything a list), look at:
set_fact:
extended_etcd_endpoints_list: "{{ groups['etcd'] | map('extract', hostvars, ['ansible_default_ipv4','address']) | map('regex_replace', '^(.*)$','https://\\1:2379') | list }}"
What is does: takes the list of all machines in the group etcd, extracts the ipv4 address, adds a prefix of 'https://' and a suffix of ':2379'. At the end, everything is transformed to a list.
cachonfinga
Updated on September 15, 2020Comments
-
cachonfinga over 3 years
I'm looking for advice. I have the following code that creates a list dynamically that I can then later use in a template.
This is a copy of the test code I put together - for the actual role I just added the admins|regex_replace variable into the j2 template.
--- - hosts: localhost gather_facts: false vars: # define empty admins var first so ansible doesn't complain admins: admin_accounts: - name: john uid: 1000 group: sysadmin shell: /bin/bash comment: "Unix Administrator" - name: paul uid: 1001 group: sysadmin shell: /bin/bash comment: "Unix Administrator" - name: george uid: 1002 group: sysadmin shell: /bin/bash comment: "Unix Administrator" - name: ringo uid: 1003 group: sysadmin shell: /bin/bash comment: "Unix Administrator" tasks: - name: build array of admin user names set_fact: admins="{{ admins}} {{ item.name }}" with_items: "{{ admin_accounts }}" # print out the fact piping through two jinja2 filters # careful with word wrapping - debug: msg={{ admins | regex_replace( '\s+',', ' ) | regex_replace`(',\s(.*)','\\1') }}`
This gives me the following:
PLAY [localhost] *************************************************************** TASK [build array of admin user names] ***************************************** ok: [localhost] => (item={u'comment': u'Unix Administrator', u'shell': u'/bin/bash', u'group': u'sysadmin', u'name': u'john', u'uid': 1000}) ok: [localhost] => (item={u'comment': u'Unix Administrator', u'shell': u'/bin/bash', u'group': u'sysadmin', u'name': u'paul', u'uid': 1001}) ok: [localhost] => (item={u'comment': u'Unix Administrator', u'shell': u'/bin/bash', u'group': u'sysadmin', u'name': u'george', u'uid': 1002}) ok: [localhost] => (item={u'comment': u'Unix Administrator', u'shell': u'/bin/bash', u'group': u'sysadmin', u'name': u'ringo', u'uid': 1003}) TASK [debug] ******************************************************************* ok: [localhost] => { "msg": "john, paul, george, ringo" } PLAY RECAP ********************************************************************* localhost : ok=2 changed=0 unreachable=0 failed=0
So...I get what I need, but am I going about it the right way?
Ansible version is 2.0.2.0 running on Centos 7.2.
Thanks in advance.
Edit: The resultant filter ended up looking like this:
- name: build list of admin user names set_fact: admin_list: "{{ admin_accounts | selectattr('state', 'equalto', 'present') | map(attribute='name') | join(', ') }}" - debug: msg={{ admin_list }}
Having added another parameter to the yaml:
state: absent
Ringo was left out, as desired.
-
cachonfinga almost 8 yearsExactly what I didn't know I was looking for, thank you so very much! The code example was created for the post, you are correct in assuming that this was for a template however I had no idea how to get the formatting I needed in the j2. Many thanks, greatly appreciated!
-
cachonfinga almost 8 yearsSorry to ask, but off that back of that, is there a filter available that would allow me to conditionally map? I'm referencing some json that is used for creating user accounts and using the "state" attribute to figure out account aliases for sudoers. Using with_items and when together achieved the desired effect. I cannot see in the ansible filters doc how I might do that without writing my own filter. I'm a little ways off that just now...
-
cachonfinga almost 8 yearsJust found a decent page on filters here and discovered selectattr and rejectattr. Unfortunately it looks like my current version of ansible uses jinja2.7, not 2.8 therefore I get "no test named 'equalto'.
-
nitzmahone almost 8 yearsYou can update jinja outside Ansible- it should be perfectly happy with 2.8
-
Tj Kellie about 6 yearsWould up vote twice if I could for steering to filters for basic outputs like this, by far the best way to generate a simple csv