Ansible: Check a list and If found, delete an item or items - ansible

I have a playbook that will run on lots of servers and on some of them, there are directories that need to be deleted. I could create a task for each server and the directories and use when inventory_hostname=XXXX but the problem is that the locations are unique along with the servers that they are on, so there would be over 300 such tasks and the output of the playbook will be swamped with all of the skips.
I thought about putting the list into defaults/main.yml, something like
vars:
server.eur.fqdn.net:
- /path/to/dir
- /path/to/other/dir
servera.nam.fqdn.net:
- /path/to/dira
server2.apac.fqdn.net:
- /path/to/dir2
- /path/to/other/dir2
and then have a task to consume the list, something like
- name: delete dir
command: rm -rf {{ item }}
with_items: {{ vars }}
when: item.key = "inventory_hostname"
But I can’t make it work.

This kind of task have to be achieved with the help of your inventory.
For example, with the inventory:
all:
children:
nodes:
hosts:
server1.example.org:
folder_to_delete:
- /path/to/folder1
- /path/to/folder2
server2.example.org:
folder_to_delete:
- /path/to/folder3
- /path/to/folder4
server3.example.org:
folder_to_delete:
- /path/to/folder5
- /path/to/folder6
And the playbook:
- hosts: nodes
gather_facts: no
tasks:
- file:
path: "{{ item }}"
state: absent
loop: "{{ folder_to_delete | default([]) }}"
We can see that the correct folders are deleted on their own nodes and that the task gives a correct feedback — ok when the folder is already absent; changed when the folder was indeed deleted:
TASK [file] **************************************************************
changed: [server1.example.org] => (item=/path/to/folder1)
ok: [server3.example.org] => (item=/path/to/folder5)
ok: [server2.example.org] => (item=/path/to/folder3)
changed: [server1.example.org] => (item=/path/to/folder2)
changed: [server3.example.org] => (item=/path/to/folder6)
ok: [server2.example.org] => (item=/path/to/folder4)
Also, by empty list as default, you could avoid having to define the variable folder_to_delete for nodes that do not need any clean up.

Put the dictionary into vars
folder_to_delete:
server.eur.fqdn.net:
- /path/to/dir
- /path/to/other/dir
servera.nam.fqdn.net:
- /path/to/dira
server2.apac.fqdn.net:
- /path/to/dir2
- /path/to/other/dir2
Use it in the task
- name: delete dir
debug:
msg: "command: rm -rf {{ item }}"
loop: "{{ folder_to_delete[inventory_hostname]|d([]) }}"
Given the inventory
shell> cat hosts
server.eur.fqdn.net
servera.nam.fqdn.net
server2.apac.fqdn.net
foo.bar.net
Example of a complete playbook for testing
shell> cat pb.yml
- hosts: all
vars:
folder_to_delete:
server.eur.fqdn.net:
- /path/to/dir
- /path/to/other/dir
servera.nam.fqdn.net:
- /path/to/dira
server2.apac.fqdn.net:
- /path/to/dir2
- /path/to/other/dir2
tasks:
- name: delete dir
debug:
msg: "command: rm -rf {{ item }}"
loop: "{{ folder_to_delete[inventory_hostname]|d([]) }}"
gives
shell> ansible-playbook pb.yml
PLAY [all] ***********************************************************************************
TASK [delete dir] ****************************************************************************
ok: [server.eur.fqdn.net] => (item=/path/to/dir) =>
msg: 'command: rm -rf /path/to/dir'
ok: [server.eur.fqdn.net] => (item=/path/to/other/dir) =>
msg: 'command: rm -rf /path/to/other/dir'
ok: [servera.nam.fqdn.net] => (item=/path/to/dira) =>
msg: 'command: rm -rf /path/to/dira'
skipping: [foo.bar.net]
ok: [server2.apac.fqdn.net] => (item=/path/to/dir2) =>
msg: 'command: rm -rf /path/to/dir2'
ok: [server2.apac.fqdn.net] => (item=/path/to/other/dir2) =>
msg: 'command: rm -rf /path/to/other/dir2'
PLAY RECAP ***********************************************************************************
foo.bar.net: ok=0 changed=0 unreachable=0 failed=0 skipped=1 rescued=0 ignored=0
server.eur.fqdn.net: ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
server2.apac.fqdn.net: ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
servera.nam.fqdn.net : ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0

Related

How Do I Make An Ansible Playbook Skip A Role?

I am trying to skip the upgrade part of my playbook. The key part looks like this:
hosts: linux_group
name: Upgrade the third-party application
roles:
- role: "upgradeEnv"
when: ENV == inventory_hostname
vars:
logdir: "/home/appuser/external/logs"
become_user: "{{ sudoUser }}"
become_method: sudo
become: yes
tags:
- upgrade
And the key part of the role looks like this:
- name: Upgrade database
shell: "upgradeDB.sh {{ env }}"
vars:
DBURL: "{{ user }}#{{ host }}"
no_log: True
register: register_appupgrade
tags:
- upgrade
- fail:
msg: "Upgrade errors:"
when: register_appupgrade.stderr !=""
tags:
- upgrade
I run the playbook with --skip-tags=upgrade but ansible still goes into the role and runs the tasks so I end up with tags: upgrade specified on each task.
The Upgrade database gets skipped but the fail ends the run due to the when condition.
Why is the role being run from the playbook even when the tags are supposed to be skipped?
Why is the fail task not being skipped?
Given the project for testing
shell> tree .
.
├── ansible.cfg
├── hosts
├── pb.yml
└── roles
└── upgradeEnv
└── tasks
└── main.yml
3 directories, 4 files
shell> cat ansible.cfg
[defaults]
inventory = $PWD/hosts
roles_path = $PWD/roles
remote_tmp = ~/.ansible/tmp
retry_files_enabled = false
stdout_callback = yaml
shell> cat hosts
[linux_group]
test_11
test_13
ansible [core 2.14.1]
The tags keyword means the tags are applied to all tasks at the indentation level.
If you apply tags at the play level
shell> cat pb.yml
- hosts: linux_group
roles:
- role: upgradeEnv
when: ENV == inventory_hostname
tags: upgrade
everything will be skipped
shell> ansible-playbook pb.yml --skip-tags=upgrade
PLAY [linux_group] ***************************************************************************
PLAY RECAP ***********************************************************************************
If you apply tags at the role level
shell> cat pb.yml
- hosts: linux_group
roles:
- role: upgradeEnv
when: ENV == inventory_hostname
tags: upgrade
the role will be skipped
shell> ansible-playbook pb.yml --skip-tags=upgrade
PLAY [linux_group] ***************************************************************************
TASK [Gathering Facts] ***********************************************************************
ok: [test_13]
ok: [test_11]
PLAY RECAP ***********************************************************************************
test_11: ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
test_13: ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=
You'll see the same result if you omit tags at role level and apply tags at each task in the role
shell> cat pb.yml
- hosts: linux_group
roles:
- role: upgradeEnv
when: ENV == inventory_hostname
shell> cat roles/upgradeEnv/tasks/main.yml
- name: Upgrade database
command: "echo {{ env }}"
register: register_appupgrade
tags: upgrade
- debug:
msg: |
register_appupgrade.stdout: {{ register_appupgrade.stdout }}
register_appupgrade.stderr: {{ register_appupgrade.stderr }}
tags: upgrade
- fail:
msg: Upgrade errors
when: register_appupgrade.stderr != ""
tags: upgrade
If you don't skip tags the play works as expected
shell> ansible-playbook pb.yml -e ENV=test_11 -e env=test
PLAY [linux_group] ***************************************************************************
TASK [Gathering Facts] ***********************************************************************
ok: [test_11]
ok: [test_13]
TASK [upgradeEnv : Upgrade database] *********************************************************
skipping: [test_13]
changed: [test_11]
TASK [upgradeEnv : debug] ********************************************************************
skipping: [test_13]
ok: [test_11] =>
msg: |-
register_appupgrade.stdout: test
register_appupgrade.stderr:
TASK [upgradeEnv : fail] *********************************************************************
skipping: [test_11]
skipping: [test_13]
PLAY RECAP ***********************************************************************************
test_11: ok=3 changed=1 unreachable=0 failed=0 skipped=1 rescued=0 ignored=0
test_13: ok=1 changed=0 unreachable=0 failed=0 skipped=3 rescued=0 ignored=0

How can I call a playbook multiple times?

I have a main login that is as bellow:
- name: Register
hosts: "{{ host }}"
tasks:
- include_role:
name: ROLE_NAME
vars:
service: "{{ service }}"
I want to call my main login multiple time something like bellow:
- name: call main logic
tasks:
- import_playbook:
name: main-logic.yml
loop:
- host: "1"
service: "s1"
- host: "2"
service: "s2"
I didn't find any solution for that, is it possible? HOW?
If you for whatever reason have to run the playbook(s) sequentially create a batch. However, if you want to execute the playbook on all hosts sequentially one after another, use serial. See Setting the batch size with serial. The difference is that you'll see PLAY RECAP once. In the first case, you'll see PLAY RECAP three times.
Create batch
For example,
shell> cat create_batch.yml
- hosts: localhost
vars:
my_batch:
- {host: host1, service: s1}
- {host: host2, service: s2}
- {host: host3, service: s3}
tasks:
- command: which bash
register: which_bash
- copy:
dest: "{{ playbook_dir }}/batch.sh"
mode: "a+x"
content: |
{{ '#!' }}{{ which_bash.stdout }}
{% for i in my_batch %}
ansible-playbook main-logic.yml -e "host={{ i.host }}" -e "service={{ i.service }}"
{% endfor %}
gives
shell> cat batch.sh
#!/usr/bin/bash
ansible-playbook main-logic.yml -e "host=host1" -e "service=s1"
ansible-playbook main-logic.yml -e "host=host2" -e "service=s2"
ansible-playbook main-logic.yml -e "host=host3" -e "service=s3"
Given the project tree for testing
shell> tree .
.
├── ansible.cfg
├── batch.sh
├── create_batch.yml
├── hosts
└── main-logic.yml
0 directories, 5 files
shell> cat ansible.cfg
[defaults]
gathering = explicit
inventory = $PWD/hosts
roles_path = $PWD/roles
remote_tmp = ~/.ansible/tmp
retry_files_enabled = false
stdout_callback = yaml
shell> cat hosts
host1
host2
host3
shell> cat main-logic.yml
- hosts: "{{ host }}"
tasks:
- debug:
msg: "{{ inventory_hostname }} {{ service }}"
The batch gives
shell> ./batch.sh
PLAY [host1] **************************************************************************************
TASK [debug] **************************************************************************************
ok: [host1] =>
msg: host1 s1
PLAY RECAP ****************************************************************************************
host1: ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
PLAY [host2] **************************************************************************************
TASK [debug] **************************************************************************************
ok: [host2] =>
msg: host2 s2
PLAY RECAP ****************************************************************************************
host2: ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
PLAY [host3] **************************************************************************************
TASK [debug] **************************************************************************************
ok: [host3] =>
msg: host3 s3
PLAY RECAP ****************************************************************************************
host3: ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
Setting the batch size with serial
Declare the below variables. For example, in group_vars/all
shell> cat group_vars/all/my_batch.yml
my_batch:
- {host: host1, service: s1}
- {host: host2, service: s2}
- {host: host3, service: s3}
my_batch_host_sevice: "{{ my_batch|
items2dict(key_name='host',
value_name='service') }}"
service: "{{ my_batch_host_sevice[inventory_hostname] }}"
Set serial: 1. The playbook will serve the remote hosts one after another
shell> cat main-logic.yml
- hosts: "{{ host }}"
serial: 1
tasks:
- debug:
var: ansible_play_batch
- debug:
msg: "{{ inventory_hostname }} {{ service }}"
gives
shell> ansible-playbook main-logic.yml -e host='host1:host2:host3'
PLAY [host1:host2:host3] **************************************************************************
TASK [debug] **************************************************************************************
ok: [host1] =>
ansible_play_batch:
- host1
TASK [debug] **************************************************************************************
ok: [host1] =>
msg: host1 s1
PLAY [host1:host2:host3] **************************************************************************
TASK [debug] **************************************************************************************
ok: [host2] =>
ansible_play_batch:
- host2
TASK [debug] **************************************************************************************
ok: [host2] =>
msg: host2 s2
PLAY [host1:host2:host3] **************************************************************************
TASK [debug] **************************************************************************************
ok: [host3] =>
ansible_play_batch:
- host3
TASK [debug] **************************************************************************************
ok: [host3] =>
msg: host3 s3
PLAY RECAP ****************************************************************************************
host1: ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
host2: ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
host3: ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0

How to iterate an item from the list using when condition in ansible

---
- name: Validate packages
hosts: localhost
tasks:
- name: Gather the rpm package facts
package_facts:
manager: auto
- set_fact:
pkg_list: "{{ ansible_facts.packages |list |unique }}"
- debug:
var: pkg_list
TASK [debug] *****************************************************************************************************************
ok: [127.0. 0.1] => {
"pkg_list": [
"libpeas",
"startup-notification",
"system-config-printer-libs",
"perl-ExtUtils-CBuilder",
"python3",
"libvirt-daemon-driver-storage-iscsi-direct",
]
}
PLAY RECAP *******************************************************************************************************************
127.0. 0.1 : ok=5 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
127.0. 0.1 : ok=4 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
Below task will provide output for one element, provided a static variable. I need to iterate over multiple elements as it should be dynamic from the list using when condition.
- name: Check whether a package is installed
debug:
msg: "{{ ansible_facts.packages['firefox'] | length }} versions of firefox are installed!"
when: "'firefox' in ansible_facts.packages"
You can use loop variables in conditionals:
- name: Check whether a package is installed
debug:
msg: "{{ ansible_facts.packages[item] | length }} versions of {{ item }} are installed!"
when: item in ansible_facts.packages
loop: "{{ pkg_list }}"

Ansible Substring From List Item

To keep this simple, I have one whitelist directory, one directory being passed in. I need to make sure the full directory of "/tmp/dir1/dir2" FAILS, where a directory of "/local/web/dir1" SUCCEEDS.
This code, always says it's good. No matter what I pass. What am I missing?
EDIT
First of all {{ dir }} is passed in, it's all running under AaaS. Data passed in looks like:
should succeed:
/local/web/test1/dir1
should fail:
/home/test
ansible-playbook-yml
---
- name: Generate Directory Structure and by list.
hosts: target_hosts
vars:
dir: {{ dir }}
whitelist_dir:
- "/local/web"
tasks:
- name: Validate Search {{ dir }}
debug:
msg: "directory is good!"
when: item is search(dir)
with_items:
- "{{ whitelist_dir }}"
Following your edit and my last comment, the only real problem I see is that you reversed your parameters in your where clause (although it does not explain IMO why it would always succeed...).
If I check with parameters in the right order, I get the result you expect. I even added a second whitelist path in the following MCVE to make sure your loop was working correctly. The following test.yml playbook
---
- name: Check if directory is in whitelist path
hosts: localhost
gather_facts: false
vars_prompt:
- name: dir
prompt: Type in full path you want to check
private: no
vars:
whitelist_dir:
- "/local/web"
- "/toto/pipo"
tasks:
- name: Validate Search {{ dir }}
debug:
msg: "directory is good!"
when: dir is search(item)
with_items:
- "{{ whitelist_dir }}"
Gives (3 differents tests)
$ ansible-playbook test.yml
Type in full path you want to check: /local/web/test1/dir1
PLAY [Check if directory is in whitelist path] *****************************************************************************************************************************************************************************************
TASK [Validate Search /local/web/test1/dir1] *******************************************************************************************************************************************************************************************
ok: [localhost] => (item=/local/web) => {
"msg": "directory is good!"
}
skipping: [localhost] => (item=/toto/pipo)
PLAY RECAP *****************************************************************************************************************************************************************************************************************************
localhost : ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
$ ansible-playbook test.yml
Type in full path you want to check: /home/test
PLAY [Check if directory is in whitelist path] *****************************************************************************************************************************************************************************************
TASK [Validate Search /home/test] ******************************************************************************************************************************************************************************************************
skipping: [localhost] => (item=/local/web)
skipping: [localhost] => (item=/toto/pipo)
skipping: [localhost]
PLAY RECAP *****************************************************************************************************************************************************************************************************************************
localhost : ok=0 changed=0 unreachable=0 failed=0 skipped=1 rescued=0 ignored=0
$ ansible-playbook test.yml
Type in full path you want to check: /toto/pipo/test.txt
PLAY [Check if directory is in whitelist path] *****************************************************************************************************************************************************************************************
TASK [Validate Search /toto/pipo/test.txt] *********************************************************************************************************************************************************************************************
skipping: [localhost] => (item=/local/web)
ok: [localhost] => (item=/toto/pipo) => {
"msg": "directory is good!"
}
PLAY RECAP *****************************************************************************************************************************************************************************************************************************
localhost : ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0

Issue adding duplicate name with different ansible_user to add_host dynamic inventory

Here is my playbook that builds a dynamic inventory using add_host:
---
- name: "Play 1"
hosts: localhost
gather_facts: no
tasks:
- name: "Search database"
command: > mysql --user=root --password=p#ssword deployment
--host=localhost -Ns -e "SELECT dest_ip,username FROM deploy_dets"
register: command_result
- name: Add hosts
add_host:
name: "{{ item.split('\t')[0] }}"
ansible_user: "{{ item.split('\t')[1] }}"
groups: dest_nodes
with_items: "{{ command_result.stdout_lines }}"
- hosts: dest_nodes
gather_facts: false
tasks:
- debug:
msg: Run the shell script with the arguments `{{ ansible_user }}` here"
The Output is good and as expected when the 'name:' attribute of add_host are of different values IPs viz '10.9.0.100' & '10.8.2.144'
$ ansible-playbook duplicate_hosts.yml
PLAY [Play 1] ***********************************************************************************************************************************************
TASK [Search database] **************************************************************************************************************************************
changed: [localhost]
TASK [Add hosts] ********************************************************************************************************************************************
changed: [localhost] => (item=10.9.0.100 user1)
changed: [localhost] => (item=10.8.2.144 user2)
PLAY [dest_nodes] *******************************************************************************************************************************************
TASK [debug] ************************************************************************************************************************************************
ok: [10.9.0.100] => {
"msg": "Run the shell script with the arguments `user1` here\""
}
ok: [10.8.2.144] => {
"msg": "Run the shell script with the arguments `user2` here\""
}
PLAY RECAP **************************************************************************************************************************************************
10.8.2.144 : ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
10.9.0.100 : ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
localhost : ok=2 changed=2 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
The problem is when the 'name:' attribute for add_host gets duplicate entry say 10.8.2.144 despite having unique 'ansible_user' value the play ignores the first name, ansible_user entry and runs only once with the latest final entry.
$ ansible-playbook duplicate_hosts.yml
PLAY [Play 1] ***********************************************************************************************************************************************
TASK [Search database] **************************************************************************************************************************************
changed: [localhost]
TASK [Add hosts] ********************************************************************************************************************************************
changed: [localhost] => (item=10.8.2.144 user1)
changed: [localhost] => (item=10.8.2.144 user2)
PLAY [dest_nodes] *******************************************************************************************************************************************
TASK [debug] ************************************************************************************************************************************************
ok: [10.8.2.144] => {
"msg": "Run the shell script with the arguments `user2` here\""
}
PLAY RECAP **************************************************************************************************************************************************
10.8.2.144 : ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
localhost : ok=2 changed=2 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
Interestingly the debug shows two entries for add_host name: 10.8.2.144 with the different ansible_users i.e 'user1' and 'user2' but when we run the group it runs just the single and latest name entry and seen in the output above.
I'm on the latest version of ansible.
Can you please provide some solution where i can run the play for every unique 'ansible_user' on the same host ?
In summary: I wish to run multiple tasks on the same host first with 'user1' and then with 'user2'
You can add a alias as inventory hostname. Here I have given the username as hostname(alias).
Please try this, I have not tested it.
- name: Add hosts
add_host:
hostname: "{{ item.split('\t')[1] }}"
ansible_host: "{{ item.split('\t')[0] }}"
ansible_user: "{{ item.split('\t')[1] }}"
groups: dest_nodes
with_items: "{{ command_result.stdout_lines }}"

Resources