Ansible with host specific files, but fallback to default files - ansible

I'm currently trying to get used to Ansible but I'm failing to achieve what seems to be a common use-case:
Lets say I have have a role nginx in roles/nginx and and one task is to setup a custom default page:
- name: install nginx default page
copy:
src: "index.html"
dest: /var/www/html/
owner: root
mode: 0644
Ansible will look for the file in:
roles/nginx/files
roles/nginx
roles/nginx/tasks/files
roles/nginx/tasks
files
./
Now for some reason a single host should receive a completely different file.
I know I could alter the file src path to src: "{{ inventory_hostname }}/index.html" but then it would search in host-specific directories only.
Is there a way to alter the search paths so that Ansible will look for files in host-specific directories first but fall-back to common directories?
I don't want to decide if files might need to be host-specific when writing roles. I'd rather like to overwrite the role default files without altering the base role at all.

Q: "Is there a way to alter the search paths so that Ansible will look for files in host-specific directories first but fall back to common directories?"
A: In general, it is not possible to change the search paths. But, with first_found it is possible to define how a specific file shall be searched. For example,
- copy:
src: "{{ lookup('first_found', findme) }}"
dest: /scratch/tmp/
owner: root
mode: 0644
vars:
findme:
- "{{ inventory_hostname }}/index.html"
- "{{ role_path }}/files/index.html"
- "{{ role_path }}/files/defaults/index.html"

Related

ansible.builtin.copy:: how to multiple file copy

So, this is ansible 2.10, and i'm trying to copy multiple files using with_items and it seems that it does not work. My tryout are more or less on the form of (this is a task):
---
- name: Sync md tools
ansible.builtin.copy:
dest: /root/bin/
src: "{{ playbook_dir }}/../additions/mdraid/{{ item }}"
with_items:
- mdadm_readd_dev
- md_check
owner: root
group: root
mode: '0700'
force: yes
I'm aware of ansible.posix.synchronize and of src_dir/ dst_dir/ format, but i would like to be able so specify a list. Is there a way to copy a specific list of multiple files with ansible.builtin.copy?
Bad syntax, ie. bad indentation. ansible-lint or ansible-playbook <playbook> -C would tell you more.

Ansible copy files with wildcard?

In my files directory I have various files, with a similar name structure:
data-example.zip
data-precise.zip
data-arbitrary.zip
data-collected.zip
I would like to transfer all of these files in the /tmp directory of my remote machine using Ansible without specifying each file name explicitly.
In other words I would like to transfer every file that stars with "data-".
What is the correct way to do that? In a similar thread, someone suggested the with_fileglob keyword, - but I couldn't get that to work.
Can someone provide me an example on how to accomplish said task?
Method 1: Find all files, store them in a variable and copy them to destination.
- hosts: lnx
tasks:
- find: paths="/source/path" recurse=yes patterns="data*"
register: file_to_copy
- copy: src={{ item.path }} dest=/dear/dir
owner: root
mode: 0775
with_items: "{{ files_to_copy.files }}"
Use remote_src: yes to copy file in remote machine from one path to another.
Ansible documentation
Method 2: Fileglob
- name: Copy each file over that matches the given pattern
copy:
src: "{{ item }}"
dest: "/etc/fooapp/"
owner: "root"
mode: 0600
with_fileglob:
- "/playbooks/files/fooapp/*"
Ansible documentation
Shortly after posting the question I actually figured it out myself. The with_fileglob keyword is the way to do it.
- name: "Transferring all data files"
copy:
src: "{{ item }}"
dest: /tmp/
with_fileglob: "data-*"

Ansible: remove files and folders while excluding some

In my Ansible Playbook I'd like to have a task that removes old files and folders from the application's directory. The twist to this otherwise simple task is that a few files or folders need to remain. Imagine something like this:
/opt/application
- /config
- *.properties
- special.yml
- /logs
- /bin
- /var
- /data
- /templates
Let's assume I'd like to keep /logs completely, /var/data and from /config I want to keep special.yml.
(I cannot provide exact code at the moment because I left work frustrated by this and, after cooling down, I am now writing up this question at home)
My idea was to have two lists of exclusions, one holding the folders and one the file. Then I use the find module to first get the folders in the application's directory into a variable and the same for the remaining files into another variable. Afterwards I wanted to remove every folder and file that are not in the lists of exclusions using the file module.
(Pseudo-YML because I'm not yet fluent enough in Ansible that I can whip up a properly structured example; it should be close enough though)
file:
path: "{{ item.path }}"
state: absent
with_items: "{{ found_files_list.files }}"
when: well, that is the big question
What I can't figure out is how to properly construct the when clause. Is it even possible like this?
I don't believe there is a when clause with the file module.
But you can probably achieve what you need as follows:
- name: Find /opt/application all directories, exclude logs, data, and config
find:
paths: /opt/application
excludes: 'logs,data,config'
register: files_to_delete
- name: Ansible remove file glob
file:
path: "{{ item.path }}"
state: absent
with_items: "{{ files_to_delete.files }}"
I hope this is what you need.
First use the find module like you said to get a total list of all files and directories. Register to a variable like all_objects.
- name: Get list of all files recursively
find:
path: /opt/application/
recurse: yes
register: all_objects
Then manually make a list of things you want to keep.
vars:
keep_these:
- /logs
- /var/data
- /config/special.yml
Then this task should delete everything except things in your list:
- name: Delete all files and directories except exclusions
file:
path: "{{ item.path }}"
state: absent
recurse: true
with_items: "{{ all_objects.files }}"
when: item.path not in keep_these
I think this general strategy should work... only thing I'm not sure about is the exact nesting heiararchy of the registered variable from the find module. You might have to play around with the debug module to get it exactly right.

Ansible: ensure directory ownership / permissions across all servers

Given a bunch of servers, A..Z, what's the best way of ensuring that all /dir/path directories on these servers (where these directories exist) are set to a certain owner, group and mode?
I can see how to do it for a specific role, e.g.
- name: Add apache vhosts configuration.
template:
src: "vhosts.conf.j2"
dest: "{{ apache_conf_path }}/{{ apache_vhosts_filename }}"
owner: root
group: root
mode: 0644
notify: restart apache
when: apache_create_vhosts
but how do you do it across a whole range of servers?
http://docs.ansible.com/ansible/latest/file_module.html
- name: Change ownership of the folder
file:
state : directory
recurse : yes
path : "{{ folder }}"
mode : "{{ desired_mode }}"
Execute the task on all the systems you want changed.
Obviously, run it as the necessary user; if that's root, make sure you specify owner and group if needed.
Forgive me if this seems a bit basic, but sometimes it's nice to have the obvious reiterated - http://docs.ansible.com/ansible/latest/intro_inventory.html
To run against a list of servers, put them in a group in your inventory file and call the task on that group as hosts.
Hosts file example:
[targetServers]
host1
host2
# etc
then in your main.yml
- name: do the thing
hosts: targetServers
# etc
then ansible-playbook -i hosts -v main.yml oe some such.

How to copy files with ansible relatively to the role?

I have a copy task inside a role and I was expecting that the src location would be relative to the role itself, not the playbook that calls the roles.
How do I make this work and use the files from myfrole/files from a task inside myrole/tasks, I don't want to include the role name as part of the path as it does not make much sense. If I do it will break if I duplicate the role.
If you do not provide any path at all, just the filename, Ansible will pick it automatically from the files directory of the role.
- copy:
src: foo.conf
dest: /etc/foo.conf
Additionally, since Ansible 1.8, there is the variable role_path which you could use in your copy task.
- copy:
src: "{{ role_path }}/files/foo.conf"
dest: /etc/foo.conf
You wouldn't need to specify the path of the file to copy, as long as it is stored in files directory.
Here's how your role should look like:
my-awesome-role
├───files
│ my-awesome-file
└───tasks
main.yml
And here's the way to call copy in the tasks/main.yml:
- copy:
src: my-awesome-file
dest: '{{ some_destination }}'

Resources