Conditionally joining two lists or taking the first and saving it in a variable - ansible

I have a role for a vpn server and some of them should be accessible to everyone, others only to admins.
Users and admins do not intersect, I want to use the variable only_admins that is True or False to toggle between these two.
I came up with this piece of configuration:
- name: Set vpn_users variable
set_fact: "vpn_users={{ users + admins}}"
when: ! only_admins
- name: Set vpn_users variable
set_fact: "vpn_users={{ admins }}"
when: only_admins
# works
- debug: "var={{ item }}"
with_items: users
# works
- debug: "var={{ item }}"
with_items: admins
# does not work
- debug: "var={{ item }}"
with_items: vpn_users
Any tips to get this working? Or other approaches?

Your syntax for set_fact is not good as per the documentation (http://docs.ansible.com/set_fact_module.html). You also use a lot of unnecessary quoting.
Here is a self-contained example that works fine :
---
- hosts: localhost
vars:
users:
- ua
- ub
admins:
- aa
- ab
tasks:
- debug: var=only_admins
- name: Set vpn_users variable to admins and users
set_fact:
vpn_users: "{{ users + admins }}"
when: not only_admins
- name: Set vpn_users variable to admins only
set_fact:
vpn_users: "{{ admins }}"
when: only_admins
- debug: var=item
with_items: users
- debug: var=item
with_items: admins
- debug: var=item
with_items: vpn_users
Try it with :
ansible-playbook test.yml --extra-vars '{ "only_admins": false }'
or :
ansible-playbook test.yml --extra-vars '{ "only_admins": true }'
Note that for debugging, you can print whatever var you'd like with the debug module. So instead of doing :
- debug: var=item
with_items: vpn_users
you just can do :
- debug: var=vpn_users
(ditto for the two preceding tasks).

This seems to work, didn't find out why the set_fact didn't work though:
- debug: "var={{ item }}"
with_items: "{{ admins if only_admins else (admins + users) }}"

Related

Ansible - loop over multiple items in stdout_lines

I am performing a grep with multiple items.
---
- hosts: my_host
gather_facts: false
vars:
my_list:
- whatever
- something
tasks:
- name: grep for item in search path
shell: "grep -rIL {{ item }} /tmp"
register: the_grep
loop: "{{ my_list }}"
- debug:
msg: "{{ item.stdout_lines }}"
loop: "{{ the_grep.results }}"
Depending on the result, multiple files could match.
msg:
- /tmp/something.conf
- /tmp/folder/file.txt
Q: How would I configure Ansible to loop over the items in stdout_lines?
The use case I'm solving is to delete .ini sections based on the item, but in this case, Ansible doesn't loop over the stdout_lines.
- name: remove stanza from ini file
ini_file:
path: "{{ item.stdout_lines }}"
section: "{{ item.item }}"
mode: '0600'
state: absent
loop: "{{ the_grep.results }}"
when: item.stdout_lines | length > 0
It seems that this doesn't work, but configuring item.stdout_lines[0] gives the partially expected result, since Ansible will use only the first item in that list. But ofc, not the 2nd and so on.
Perhaps there's a prettier answer, but solved it by using with_nested and creating a json_query:
- name: remove stanza from ini file
ini_file:
path: "{{ item.0 }}"
section: "{{ item.1.item }}"
mode: '0600'
state: absent
with_nested:
- "{{ the_grep | json_query('results[].stdout_lines[]') }}"
- "{{ the_grep.results }}"

(solved) Ansible loop (subelements) in users>name, users>authorized_key_file and users>authorized_keyS

i tried this test code :
- hosts: localhost
gather_facts: no
vars:
users:
- name: user1
authorized_keys_file: authorized_keys_file_1.log
authorized_keys:
- key1
- key2
- name: user2
authorized_keys_file: authorized_keys_file_2.log
authorized_keys:
- key1
tasks:
- name: List keys to add to authorized_key_file
debug:
msg: "User={{ item.0.name }} File={{ item.0.authorized_keys_file }} key={{ item.1 }}"
loop:
- "{{ users | subelements('authorized_keys') }}"
but i got the error :
The task includes an option with an undefined variable. The error was: 'list object' has no attribute 'name'
expected result (to be used in ansible.posix.authorized_key) :
User=user1 File=authorized_keys_file_1 key=key1
User=user1 File=authorized_keys_file_1 key=key2
User=user2 File=authorized_keys_file_2 key=key1
Do you have an idea of what to do !?
Regards.
I found the solution ! "loop" must be on one line.
This is because i did a conversion from the old "with_subelements"
old version :
with_subelements:
- "{{ users }}"
- authorized_keys
New version (oneline...) :
loop: "{{ users | subelements('authorized_keys') }}"

A method for listing Ansible modules used by playbooks

I'm creating a requirements.yml in an Ansible project, and I want to identify all of the modules that need to be installed from ansible-galaxy that are used by project playbooks. ansible-doc --list --playbook-dir foo seems like the right tool for the job, but it lists all locally available modules, not just the ones which are actually used in the foo directory. ansible-galaxy list doesn't account for any which are needed but not installed.
Is there a way to do this where I don't end up writing a shell script to sed|awk|grep the info I want?
The best approach I've been able to come up with so far is to ansible-playbook --syntax-check each of the playbooks. This will throw errors such as
ERROR! the role 'foo' was not found ...
ERROR! couldn't resolve module/action 'bar'. This often indicates a misspelling, missing collection, or incorrect module path.
but this is not ideal because it exits as soon as any error occurs. I have to fix each one and run the syntax check again.
FWIW, as a concept, the playbook below lists the modules used in a role
- hosts: localhost
vars:
keywords:
- always
- become
- block
- loop
- loop_control
- name
- notify
- register
- tags
- vars
- when
tasks:
- name: The variable my_role_path is mandatory
assert:
that: my_role_path|d('')|length > 0
- name: Find tasks files
find:
path: "{{ my_role_path }}/tasks"
patterns: '*.yml,*.yaml'
recurse: true
register: result
- name: Create list of tasks
set_fact:
lft: "{{ lft|d([]) + lookup('file', item)|from_yaml }}"
loop: "{{ result.files|map(attribute='path')|list }}"
- name: Get list of keys
set_fact:
lfk: "{{ lfk|d([]) + item.keys()|list }}"
loop: "{{ lft }}"
- name: Get list of keys from block/rescue/always
set_fact:
lfk: "{{ lfk|d([]) + item.keys()|list }}"
loop: "{{ lft|json_query('[].[block, rescue, always]')|flatten }}"
- name: Display list of modules
debug:
var: lfk|unique|sort|difference(keywords)
For example, analyze the role systemd
shell> ansible-playbook pb.yml -e my_role_path=roles/ansible-role-systemd
...
lfk|unique|sort|difference(keywords):
- command
- file
- include_tasks
- meta
- systemd
- template
Complete the list of the keywords.
Use the tasks below to analyze a playbook
tasks:
- name: The variable my_playbook_path is mandatory
assert:
that: my_playbook_path|d('')|length > 0
- name: Create list of tasks
set_fact:
lft: "{{ _playbook|map(attribute='tasks')|flatten }}"
vars:
_playbook: "{{ lookup('file', my_playbook_path)|from_yaml }}"
- name: Get list of keys
set_fact:
lfk: "{{ lfk|d([]) + item.keys()|list }}"
loop: "{{ lft }}"
- name: Get list of keys from block/rescue/always
set_fact:
lfk: "{{ lfk|d([]) + item.keys()|list }}"
loop: "{{ lft|json_query('[].[block, rescue, always]')|flatten }}"
- name: Display list of modules
debug:
var: lfk|unique|sort|difference(keywords)
For example, analyzing the first playbook gives
lfk|unique|sort|difference(keywords):
- assert
- debug
- find
- set_fact

set path when file exists in Ansible yml code

I'm trying to set a var only when a file exists, here is one of my attempts
---
- hosts: all
tasks:
- stat:
path: '{{ srch_path_new }}/bin/run'
register: result
- vars: srch_path="{{ srch_path_new }}"
when: result.stat.exists
This also didn't work
- vars: srch_path:"{{ srch_path_new }}"
The task you are looking for is called set_fact: and is the mechanism ansible uses to declare arbitrary "host variables", sometimes called "hostvars", or (also confusingly) "facts"
The syntax would be:
- set_fact:
srch_path: "{{ srch_path_new }}"
when: result.stat.exists
Also, while vars: is a legal keyword on a Task, its syntax is the same as set_fact: (or the vars: on the playbook): a yaml dictionary, not a key:value pair as you had. For example:
- debug:
msg: hello, {{ friend }}
vars:
friend: Jane Doe
and be aware that vars: on a task exist only for that task

Adding field to dict items

Consider the following play. What I am trying to do is add a field, tmp_path which is basically the key and revision appended together to each element in the scripts dict.
---
- hosts: localhost
connection: local
gather_facts: no
vars:
scripts:
a.pl:
revision: 123
b.pl:
revision: 456
tasks:
- with_dict: "{{ scripts }}"
debug:
msg: "{{ item.key }}_{{ item.value.revision }}"
# - with_items: "{{ scripts }}"
# set_fact: {{item.value.tmp_path}}="{{item.key}}_{{item.value.revision}}"
# - with_items: "{{ scripts }}"
# debug:
# msg: "{{ item.value.tmp_path }}"
...
Obviously the commented code doesn't work, any idea how I can get this working? Is it possible to alter the scripts dict directly, or should I somehow be creating a new dict to reference instead?
By the way welcome to correct the terminology for what I am trying to do.
OK, I think I got a solution (below), at least to let me move forwards with this. Disadvantages are it has removed the structure of my dict and also seems a bit redundant having to redefine all the fields and use a new variable, If anyone can provide a better solution I will accept that instead.
---
- hosts: localhost
connection: local
gather_facts: no
vars:
scripts:
a.pl:
revision: 123
b.pl:
revision: 456
tasks:
- with_dict: "{{ scripts }}"
debug:
msg: "{{ item.key }}_{{ item.value.revision }}"
- with_dict: "{{ scripts }}"
set_fact:
new_scripts: "{{ (new_scripts | default([])) + [ {'name': item.key, 'revision': item.value.revision, 'tmp_path': item.key ~ '_' ~ item.value.revision}] }}"
# - debug:
# var: x
# - with_dict: "{{ scripts }}"
- with_items: "{{ new_scripts }}"
debug:
msg: "{{ item.tmp_path }}"
...
BTW credit to the following question which pointed me in the right direction:
Using Ansible set_fact to create a dictionary from register results

Resources