Ansible - Recursive copy - ansible

I'm trying to copy the content of my directories (located on my bastion) on a server but it doesn't work.
I tried with "with_fileglob:", "with_items:" but each times, I had the error :
"'item' is undefined"
I don't understand why.
Ths is my code which doesn't work:
tasks:
- name: Copy directories...
copy:
src: "{{ item }}"
dest: "{{ dir_dest }}/"
owner: "{{ dir_owner }}"
group: "{{ dir_group }}"
mode: '0755'
with_fileglob:
- /home/ansible/delivery/my_dir/*
whereas this one works :
tasks:
- name: Copy directories...
copy:
src: "/home/ansible/delivery/my_dir/"
dest: "{{ dir_dest }}/"
owner: "{{ dir_owner }}"
group: "{{ dir_group }}"
mode: '0755'
But I can't use this 2nd solution because I have a lot a directories to copy.
Thank for your help.

tasks:
- name: Copy directories...
copy:
src: "{{ item }}"
dest: "{{ dir_dest }}/"
owner: "{{ dir_owner }}"
group: "{{ dir_group }}"
mode: '0755'
with_fileglob:
- /home/ansible/delivery/my_dir/*
Try this.

Related

In Ansible loop, test existence of files from registered results

I have several files that I need to backup in different directories. I have tried the code below and not working for me.
vars:
file_vars:
- {name: /file1}
- {name: /etc/file2}
- {name: /etc/file/file3}
tasks:
- name: "Checking if config files exists"
stat:
path: "{{ item.name }}"
with_items: "{{ file_vars }}"
register: stat_result
- name: Backup Files
copy: src={{ item.name }} dest={{ item.name }}{{ ansible_date_time.date }}.bak
with_items: "{{ file_vars }}"
remote_src: yes
when: stat_result.stat.exists == True
The problem is the condition
when: stat_result.stat.exists == True
There is no attribute stat_result.stat. Instead, the attribute stat_result.results is a list of the results from the loop. It's possible to create a dictionary of files and their statuses. For example
- set_fact:
files_stats: "{{ dict(my_files|zip(my_stats)) }}"
vars:
my_files: "{{ stat_result.results|json_query('[].item.name') }}"
my_stats: "{{ stat_result.results|json_query('[].stat.exists') }}"
Then simply use this dictionary in the condition
when: files_stats[item.name]
Below is a shorter version which creates the dictionary more efficiently
- set_fact:
files_stats: "{{ dict(stat_result.results|
json_query('[].[item.name, stat.exists]')) }}"
Please try using below worked for me:
---
- name: Copy files
hosts: localhost
become: yes
become_user: root
vars_files:
- files.yml
tasks:
- name: "Checking if config files exists"
stat:
path: "{{ item }}"
with_items: "{{ files }}"
register: stat_result
- name: Ansible
debug:
msg: "{{ stat_result }}"
- name: Backup Files
copy:
src: "{{ item }}"
dest: "{{ item.bak }}"
with_items: "{{ files }}"
when: stat_result == "True"
and files.yml will look like:
---
files:
- /tmp/file1
- /tmp/file2
you can check you playbook syntax using below command:
ansible-playbook copy.yml --syntax-check
Also you do dry run your playbook before actual execution.
ansible-playbook -i localhost copy.yml --check

Is it possible to install custom Ansible plugin from git

I'd like to share a custom inventory plugin across multiple playbooks and users.
Is it possible to host a custom inventory plugin on git and the like roles with requirements.yml do something like:
ansible-galaxy install -r requirements.yml
I tried to embbed it in a role using:
/myrole/library/inventory_plugins/custom_inventory.py
/myrole/plugins/inventory_plugins/custom_inventory.py
/myrole/inventory_plugins/custom_inventory.py
but so far no luck.
Q: "Is it possible to install custom Ansible plugin from git?"
A: Yes. It's possible. For example
1) Download and extract the plugins
vars:
ma_src_path: "/usr/local/ansible/src"
ma_plugins_path: "/usr/local/ansible/plugins"
map_mitogen_ver: 0.2.8
map_mitogen_sha256: "sha256:1bfca66bcc522346c9167a3a9829feac5ee3b84431e49354fb780e4b9a4b0eee"
ma_plugins:
- archive: mitogen-{{ map_mitogen_ver }}.tar.gz
archive_url: https://networkgenomics.com/try/mitogen-{{ map_mitogen_ver }}.tar.gz
checksum: "{{ map_mitogen_sha256 }}"
plugins:
- path: mitogen-{{ map_mitogen_ver }}/ansible_mitogen/plugins/strategy
ini_key: strategy_plugins
enable: true
tasks:
- name: "plugins: Download archives"
get_url:
url: "{{ item.archive_url }}"
dest: "{{ ma_src_path }}"
checksum: "{{ item.checksum }}"
loop: "{{ ma_plugins }}"
- name: "plugins: Extract archives"
unarchive:
src: "{{ ma_src_path }}/{{ item.archive }}"
dest: "{{ ma_plugins_path }}"
loop: "{{ ma_plugins }}"
2) Configure the plugins with template ansible-plugins.cfg.j2
vars:
ma_config:
- path: "/etc/ansible/ansible.cfg"
template: "ansible-plugins.cfg.j2"
owner: "root"
group: "root"
mode: "0644"
config:
- { section: "defaults", key: "inventory", value: "/etc/ansible/hosts" }
- { section: "defaults", key: "strategy", value: "mitogen_linear" }
tasks:
- name: "configure: Ansible configuration from template"
template:
src: "{{ item.template }}"
dest: "{{ item.path }}"
owner: "{{ item.owner }}"
group: "{{ item.group }}"
mode: "{{ item.mode }}"
backup: "{{ ma_backup_conf }}"
loop: "{{ ma_config }}"
when: ma_config|length > 0
See the complete role at Ansible Galaxy.

How do I define multiple directories maintaining directory-specific attributes in a proper way?

Take the following as an example:
- name: setup jitsi-meet volumes (0/5)
file:
path: /srv/jitsi-meet/.jitsi-meet-cfg
state: directory
owner: root
group: root
mode: 0755
- name: setup jitsi-meet volumes (1/5)
file:
path: /srv/jitsi-meet/.jitsi-meet-cfg/web
state: directory
owner: 2000
group: 2000
mode: 0644
- name: setup jitsi-meet volumes (2/5)
file:
path: /srv/jitsi-meet/.jitsi-meet-cfg/prosody
state: directory
owner: root
group: root
mode: 0600
Is there a proper way in ansible to not only reduce these three separate tasks to one single task, but to maintain the possibility to modify owner, group and mode for each path?
An option would be to loop the list of files.
vars:
my_files_dir: /srv/jitsi-meet/.jitsi-meet-cfg
my_files:
- path: "{{ my_files_dir }}"
state: directory
owner: "root"
group: "root"
mode: "0755"
- path: "{{ my_files_dir }}/web"
state: directory
owner: "2000"
group: "2000"
mode: "0644"
- path: "{{ my_files_dir }}/prosody"
state: directory
owner: "root"
group: "root"
mode: "0600"
tasks:
- file:
path: "{{ item.path }}"
state: "{{ item.state }}"
owner: "{{ item.owner }}"
group: "{{ item.group }}"
mode: "{{ item.mode }}"
loop: "{{ my_files }}"
This can be simplified further with the parameters' default values
vars:
my_files_dir: /srv/jitsi-meet/.jitsi-meet-cfg
my_files:
- path: "{{ my_files_dir }}"
- path: "{{ my_files_dir }}/web"
owner: "2000"
group: "2000"
mode: "0644"
- path: "{{ my_files_dir }}/prosody"
mode: "0600"
tasks:
- file:
path: "{{ item.path }}"
state: "{{ item.state|default('directory') }}"
owner: "{{ item.owner|default('root') }}"
group: "{{ item.group|default('root') }}"
mode: "{{ item.mode|default('0755') }}"
loop: "{{ my_files }}"

looping using with_fileglob

How do i loop using "with_fileglob". I am trying to copy files matching wildcard, but with different permissions at the destination.
- hosts: myhost
gather_facts: no
tasks:
- name: Ansible copy test
copy:
src: "{{ item.origin }}"
dest: /home/user1/tmps/
owner: user1
mode: "{{item.mode}}"
with_fileglob:
- { origin: '/tmp/hello*', mode: '640'}
- { origin: '/tmp/hi*', mode: '600'}
It throws error as below:
An exception occurred during task execution. To see the full traceback, use
-vvv. The error was: AttributeError: 'dict' object has no attribute 'rfind'
I think the cleanest way is to implement this, would be a nested loop with include_tasks.
Where you main playbook file contains:
...
vars:
my_patterns:
- origin: "/tmp/hello*"
mode: "0640"
- origin: "/tmp/hi*"
mode: "0600"
tasks:
- include_tasks: "my_glob.yml"
with_items: "{{ my_patterns }}"
loop_control:
loop_var: my_pattern
...
and a subordinate my_glob.yml-tasks file:
---
- name: Ansible copy test
copy:
src: "{{ item }}"
dest: /home/user1/tmps/
owner: user1
mode: "{{ my_pattern.mode }}"
with_fileglob: "{{ my_pattern.origin }}"
Alternative method
Using Jinja2 to populate a list of objects { 'path': '...', 'mode': '...' }' based on fileglob-lookup plugin results.
vars:
my_patterns:
- origin: '/tmp/hello*'
mode: '0640'
- origin: '/tmp/hi*'
mode: '0600'
tasks:
- copy:
src: "{{ item.paht }}"
dest: /home/user1/tmps/
owner: user1
mode: "{{ item.mode }}"
with_items: "[{% for match in my_patterns %}{% for file in lookup('fileglob', match.origin, wantlist=True) %}{ 'path':'{{ file }}','mode':'{{ match.mode }}'}{% if not loop.last %},{% endif %}{% endfor %}{% if not loop.last %},{% endif %}{% endfor %}]"
The above works if patterns are matched, you'd need to add checks if the results are not empty.
according to the documentation, you cant pass to the fileglob a dictionary variable, adding the desired file permissions after copy as you have attempted (i mean the declaration { origin: '/tmp/hello*', mode: '640'}).
simple module call that will work for you:
- hosts: localhost
gather_facts: no
tasks:
- name: Ansible copy test
copy:
src: "{{ item }}"
dest: /SAMBA_ROOT/TEMP/
owner: root
with_fileglob:
- '/tmp/hello*'
- '/tmp/hi*'
if you want to have each of the file group have different file permissions, i suggest you use 2 different calls where the mode is "hardcoded", for example:
- hosts: localhost
gather_facts: no
tasks:
- name: copy hello files
copy:
src: "{{ item }}"
dest: /SAMBA_ROOT/TEMP/
owner: root
mode: 0640
with_fileglob:
- '/tmp/hello*'
- name: copy hi files
copy:
src: "{{ item }}"
dest: /SAMBA_ROOT/TEMP/
owner: root
mode: 0600
with_fileglob:
- '/tmp/hi*'

Ansible: when condition with_items

I have such a part of a playbook, which works fine:
- name: "Copy solrconfig.xml"
copy:
src: "{{role_path}}/files/{{item.path}}"
dest: "{{solr_jmx_config}}/solrconfig.xml"
with_items:
- path: solrconfig_master.xml
when: inventory_hostname == "{{ solr_master }}"
become: yes
become_user: solr
- name: "Copy solrconfig.xml"
copy:
src: "{{role_path}}/files/{{item.path}}"
dest: "{{solr_jmx_config}}/solrconfig.xml"
with_items:
- path: solrconfig_slave.xml
when: inventory_hostname != "{{ solr_master }}"
become: yes
become_user: solr
However, I would like it to look nicer and do something like that:
- name: "Copy solrconfig.xml"
copy:
src: "{{role_path}}/files/{{item.path}}"
dest: "{{solr_jmx_config}}/solrconfig.xml"
with_items:
- path: solrconfig_master.xml -> when: inventory_hostname == "{{ solr_master }}"
- path: solrconfig_slave.xml -> when: inventory_hostname != "{{ solr_master }}"
become: yes
become_user: solr
How to apply "when" condition for each particular item?
Best regards,
Marek
Why do you need with_items and role_path here? Try this:
- name: "Copy solrconfig.xml"
copy:
src: "{{ 'solrconfig_master.xml' if inventory_hostname == solr_master else 'solrconfig_slave.xml' }}"
dest: "{{solr_jmx_config}}/solrconfig.xml"
become: yes
become_user: solr
with_ is generally used for loops, you don't need it for single element.
When you call tasks inside roles, search path for file modules include <role_path>/files/....

Resources