90

I'm designing a kind of playbook lib with individual tasks

so in the usual roles repo, I have something like:

roles
├── common
│   └── tasks
│       ├── A.yml
│       ├── B.yml
│       ├── C.yml
│       ├── D.yml
│       ├── login.yml
│       ├── logout.yml
│       └── save.yml
├── custom_stuff_workflow
│   └── tasks
│       └── main.yml
└── other_stuff_workflow
    └── tasks
        └── main.yml

my main.yml in custom_stuff_workflow then contain something like:

---

- include: login.yml
- include: A.yml
- include: C.yml
- include: save.yml
- include: logout.yml

and this one in the other workflow:

---

- include: login.yml
- include: B.yml
- include: A.yml
- include: D.yml
- include: save.yml
- include: logout.yml

I can't find a way to do it in a natural way: one way that worked was having all tasks in a single role and tagging the relevant tasks while including a custom_stuff_workflow

The problem I have with that is that tags cannot be set in the calling playbook: it's only to be set at command line as I'm distributing this ansible repo with many people in the company, I can't rely on command line invocations (it would be nice to have a #! header in yml to be processed by ansible-playbook command)

I could also copy the relevant tasks (inside common in the above tree) in each workflow, but I don't want to repeat them around

Can someone see a solution to achieve what I'd like without repeating the tasks over different roles?

I guess the corner stone of my problem is that I define tasks as individual and it looks not natural in ansible...

Thanks a lot

PS: note that the tasks in the workflow have to be done in specific order and the only natural steps to abstract would be the login and save/logout

PPS: I've seen this question How do I call a role from within another role in Ansible? but it does not solve my problem as it's invoking a full role and not a subset of the tasks in a role

Community
  • 1
  • 1
Louis
  • 1,392
  • 2
  • 11
  • 16
  • as per https://coderwall.com/p/dlsjya/executable-playbooks, we can put a shebang comment on top of the yml file, but again nothing prevents some colleague to run directly `ansible-playbook` on the file instead of running it as an executable (on top of portability issues) – Louis May 12 '15 at 14:41
  • maybe the command line can be a helper. I use a perl script to run diffrent playbooks. (not exactly the same). The call from the script looks like: `ansible-playbook name_of_playbook --extra-vars "lots of vars" `, if it would be possible for you to specify the way your colleagues use it, thats maybe a way. – ThoFin May 12 '15 at 15:16
  • indeed http://stackoverflow.com/users/4503915/thofin, but if I can find a native solution it would be much nicer – Louis May 12 '15 at 15:33

4 Answers4

89

Just in case someone else bumps into this, version 2.2 of Ansible now has include_role. You can now do something like this:

---
- name: do something
  include_role:
    name: common
    tasks_from: login

Check out the documentation here.

Kevin C
  • 4,851
  • 8
  • 30
  • 64
uLan
  • 1,099
  • 9
  • 12
  • 1
    [This doesn't work inside handlers](https://github.com/ansible/ansible/issues/20493). For those, you'll still need to use part (2) from tehK's answer. – spiffytech Mar 23 '18 at 18:27
  • 1
    Thank you! That's exactly what I was looking for. With this command we're now able to create kind of shared task library. There's much more place for DRY principle in ansible now. – realmaniek Aug 02 '18 at 09:17
  • 1
    Handlers are now included in Ansible 2.8 – bacrossland Jun 07 '19 at 15:29
  • You can also use this in an ad hoc manner with `ansible ... -m include_role -a "name=common tasks_from=login"` – Ed Kirk May 26 '20 at 16:03
  • so this works when called from a task in a role . eg I'm in role "config hardware" and I need to reuse a playbook from role "install os" called "reboot.yml". this will work for that case? – majorgear Mar 02 '22 at 18:49
49

Yes, Ansible doesn't really like tasks as individual components. I think it wants you to use roles, but I can see why you wouldn't want to use roles for simple, reusable tasks.

I currently see two possible solutions:

1. Make those task-files into roles and use dependencies

Then you could do something like this in e.g. custom_stuff_workflow

dependencies:
  - { role: login }

See: https://docs.ansible.com/playbooks_roles.html#role-dependencies

2. Use include with "hardcoded" paths to the task files

- include: ../../common/tasks/login.yml

That worked pretty well in a short test playbook I just did. Keep in mind, you can also use parameters etc. in those includes.

See: http://docs.ansible.com/ansible/latest/playbooks_reuse.html

I hope I understood that question correctly and this helps.

Community
  • 1
  • 1
tehK
  • 2,265
  • 4
  • 17
  • 16
  • 5
    Thanks for your answer tehk, I've tried these 2 solutions but obviously it's not perfect I may go for the 2nd one, but the hardcoded path is really not something appealing – Louis May 17 '15 at 14:18
  • The first option is really good. It's clear what each role does and forces you to break your project up into smaller modules – jonatan Jun 10 '15 at 12:16
  • 5
    If I understand correctly, dependencies are not flexible: they run *before* the role. With 'include' you can run the included code at any step inside your role. – FuzzyAmi Sep 29 '15 at 13:32
  • include supports parameter. But is there any way to use `register` with include? – Shiplu Mokaddim Dec 02 '15 at 09:43
  • I have faced similar issue and the 2nd options quite works... but it is still feels like a walkaround. – Atais Sep 29 '16 at 09:09
  • 2
    After reading what you guys tried, I am about to symlink the task files instead of hardcode the path. Does it have any obvious downside that I missed? – alanjds Nov 03 '16 at 14:37
  • I think the 2nd option has the benefit (or downside) of using the local files directories, in the role including the tasks. – Enno Gröper May 29 '19 at 07:44
1

Using include_role: with option tasks_from is a good idea. However this still includes parts of the role. For example it loads role vars and meta dependencies. If apply is used to apply tags to an included file, then same tags are applied to meta dependencies. Also, the ansible output lists as the included role's name in its output, which is confusing.

It is possible to dynamically locate a role and include a file using first_found. One can find the role path searching DEFAULT_ROLES_PATH and load a file from tasks folder. Ansible uses the same variable when sarching a role, so long as the role is in a path that Ansible can find, then the file will be loaded.

This method is as dynamic as using include_role with option tasks_from

Example:

- name: Include my_tasks.yml from my_ansible_role
  include_tasks: "{{lookup('first_found', params)}}"
  vars:
    params:
      files: my_ansible_role/tasks/my_tasks.yml
      paths: "{{ lookup('config', 'DEFAULT_ROLES_PATH') }}"
Evren Yurtesen
  • 2,267
  • 1
  • 22
  • 40
1

You can use the built-in variable playbook_dir to include tasks from other roles.

- name: "Create SSL certificates."
  include_tasks: "{{ playbook_dir }}/roles/common/tasks/ssl.yml"
  with_items: "{{ domains }}"

The ssl.yml is in the common role. It can have any number of tasks; also, passing variables like the example is possible.

Madan Sapkota
  • 25,047
  • 11
  • 113
  • 117