26

Ansible Best Practices described that every role contains file directory that have all files needed by this rule.

In my case I have different roles that share the same files. But I cannot make a copy of these files in each role as there will be no one source of these files and if edit happens to one of them it will become tedious to make this change for every role.

A solution I made is to create another folder and reference it using absolute or relative path. Is this the best way of doing it?

My ansible directory look like this

play.yml
roles/
  web/
    tasks/
    files/
      common-1
      common-2
      other-multiple-files
  role-2/
    tasks/
    files/
      common-1
      common-2
      other-multiple-files
  role-3/
    tasks/
      files/
        common-2
  role-4/
    tasks/
      files/
        common-1
ydaetskcoR
  • 53,225
  • 8
  • 158
  • 177
Nasr
  • 2,482
  • 4
  • 26
  • 31
  • refactor out the tasks of the two roles using the common files in a new role, then have this new role as a dependency in meta/main.yml for both – guido Dec 15 '15 at 12:26
  • @ᴳᵁᴵᴰᴼ if two rules has same files common between them i could do this, but if i have more rules that contain some of the files creating role as a dependency will not be applicable. sorry i wasn't clear enough about it, i added two more rules to the question to make my point clear – Nasr Dec 15 '15 at 12:34

6 Answers6

35

You've got two reasonably decent approaches you can try here to reduce repetition.

You could have a separate shared-files directory that sits as a sibling to your role folders like this:

play.yml
roles/
  web/
    tasks/
    files/
      other-multiple-files
  role-2/
    tasks/
    files/
      other-multiple-files
  role-3/
    tasks/
  role-4/
    tasks/
  shared-files/
    common-1
    common-2

You would then reference this in the tasks with a relative file location from where the role/files folder would be:

- name: copy common-1
  copy:
    src: ../../common-1
    dest: /path/to/dest/common-1

- name: copy role specific file
    src: other-multiple-files
    dest: /path/to/dest/other-multiple-files

Or alternatively to use a relative path to the folder, you could symlink things across like this:

play.yml
roles/
  web/
    tasks/
    files/
      common-1 -> ../../common-1
      common-2 -> ../../common-2
      other-multiple-files
  role-2/
    tasks/
    files/
      common-1 -> ../../common-1
      common-2 -> ../../common-2
      other-multiple-files
  role-3/
    tasks/
    files/
      common-2 -> ../../common-2
  role-4/
    tasks/
    files/
      common-1 -> ../../common-1
  shared-files/
    common-1
    common-2

And you can then reference the file as if it was in the role/files directory directly:

- name: copy common-1
  copy:
    src: common-1
    dest: /path/to/dest/common-1

- name: copy role specific file
    src: other-multiple-files
    dest: /path/to/dest/other-multiple-files
ydaetskcoR
  • 53,225
  • 8
  • 158
  • 177
  • 2
    I really liked symbolic link approach, it will make roles is free from paths, and the path will only set once in symbolic link. – Nasr Dec 15 '15 at 14:18
  • I use symbolic links quite a bit in my roles to share files, templates, etc. among them. git handles symlinks nicely, so relative symlinks among the various files & templates directories is a piece of cake. – Bruce P Dec 15 '15 at 15:00
  • 11
    your src: `../../common-1` should be `src: ../../shared-files/common-1` – Manuel Rozier May 17 '19 at 08:48
  • How do we create a symbolic link in ansible roles/files? do we have to set it in ansible or do we have to configure it in client machines? – CKS Nov 20 '19 at 18:18
  • It's just on the filesystem. If you keep your Ansible code in git then it will handle setting symlinks up for all users of the repository. – ydaetskcoR Nov 20 '19 at 19:08
  • Yes, please fix your broken "solution" by including a correct relative path ../../shared-files/common.txt for example. – Rick O'Shea Nov 22 '19 at 19:27
17

My solution was to create roles for the common stuff and add them as dependencies.

For example, your playbook would look like this

play.yml
roles/
  common-1/
    files/
      common-1
  common-2/
    files/
      common-2
  web/
    meta/
      common-1
      common-2
    tasks/
    files/
      other-multiple-files
  role-2/
    meta/
      common-1
      common-2
    tasks/
    files/
      other-multiple-files
  role-3/
    meta/
      common-2
    tasks/
  role-4/
    meta/
      common-1
    tasks/

so, roles/common-1 and roles/common-2 are roles that just deploy the files, and all the roles that need those, they have it as a dependency in the meta/ folder.

user2599522
  • 3,005
  • 2
  • 23
  • 40
  • Could be useful when trying to import template from another role using `{%- import "file.j2" -%}` where relative paths are not accepted. – cermatej Jul 16 '19 at 14:02
3

Ansible actually supports project-wide common folders. There is no need to create custom-named folders. See the answer by and in this other question Ansible: Global template folder?

A tasks/ folder at the ansible/ root level is also accepted.

On a side note, I wasn't able to find any official documentation on this sort of directory structure https://docs.ansible.com/ansible/latest/user_guide/playbooks_best_practices.html#directory-layout

user2066480
  • 1,229
  • 2
  • 12
  • 24
  • 1
    Unfortunately, Ansible seems to have dropped support for this feature (apparently, it was never intended). See https://github.com/ansible/ansible/issues/20442 and https://stackoverflow.com/a/46398991/1198249. – solarchemist Jan 29 '21 at 22:37
2

I use some given ansible variables like inventory_dir and place the files in relation to this path. So then I define a variable

common_files_path: "{{ inventory_dir }}/resources/shared_files"

... and then use it in the ansible task

- name: get local files
  command: cat {{ item }}
  register: some_files
  with_fileglob: "{{ common_files_path }}/*"
  delegate_to: localhost
papanito
  • 2,349
  • 2
  • 32
  • 60
1

There's one other way that hasn't been discussed here, yet. You can create a "files" and other directories in the same folder where the the main playbooks are located, and use the playbook_dir special variable to access them.

For example:

play.yml
files/
  common-1.py
roles/
  role-1/
    tasks/
      main.yml

Then, in roles/role-1/tasks/main.yml run the common scripts from the play-level "files" directory like this:

- name: Run common-1.py
  command: "{{ playbook_dir }}/files/common-1.py"
  changed_when: false
V P
  • 318
  • 2
  • 5
0

The "src" path is relative to the "files" directory in your role. This is why file roles/bar/files/foo.txt is picked up by simply specifying foo.txt as your src. By extension, relative paths are relative to the files folder. Sot the location of src: ../../../../foo/bar.txt is crystal clear. If all your roles need bar.txt they can all provide that same relative path since all your roles are siblings.

Rick O'Shea
  • 1,410
  • 19
  • 15