Table of Contents
Go from Ansible beginner to Ansible pro with this full video course.
You can use the Ansible include_tasks module
to import tasks from other files. There are a number of reasons you might want to move tasks into separate files when using Ansible:
- Refactoring - chop up unfeasibly large playbooks into smaller files to make them easier to comprehend
- Conditionally including tasks - you may wish to run groups of tasks conditionally based on some host-specific property (e.g. the OS)
- Running a list of tasks for each item in a loop
I use the include_tasks
module frequently and I’ve included my most common use cases in the sections below.
Common use cases
My most common use cases for using include_tasks
are:
- Running different tasks depending on the OS of the host (e.g. Debian-based vs RHEL-based operating systems)
- Splitting a role’s
main.yml
into smaller, more manageable files
How to include tasks from a file
The most basic function of the include_tasks
module is to import tasks from a file into another list of tasks. This could be within a playbook or a role’s task files. The most common use of this technique is to break up larger lists of tasks into smaller, more manageable files.
For example, imagine you have a list of tasks like the following:
---
# ./roles/my_role/tasks/main.yml
- name: install dependencies
apt:
name: "{{ item }}"
state: present
become: true
loop:
- nginx
- php7.2
- name: configure the application
copy:
src: my_app.conf
dest: /etc/my_app.conf
become: true
This is only 2 tasks, but for the sake of the example you can break these tasks into their own files:
---
# ./roles/my_role/tasks/install.yml
- name: install dependencies
apt:
name: "{{ item }}"
state: present
become: true
loop:
- nginx
- php7.2
---
# ./roles/my_role/tasks/configure.yml
- name: configure the application
copy:
src: my_app.conf
dest: /etc/my_app.conf
become: true
You can now rewrite the original list of tasks like this:
---
# ./roles/my_role/tasks/main.yml
- include_tasks: install.yml
- include_tasks: configure.yml
If you ever need to add more tasks to set up or configure the role, you can add them directly to the setup.yml
or configure.yml
instead of the main.yml
.
Using this technique is also a nice way to refactor large lists of tasks into smaller logical groupings (i.e. this group of tasks installs things and that group of tasks configures things).
How to include tasks conditionally
Another very common use case for include_tasks
is to import some tasks conditionally based on some variable in a role.
A common example would be to give the user the ability to choose whether to install a piece of software using a package manager like apt
or yum
or by compiling it from source.
Use the when
keyword to include tasks conditionally:
# ./roles/my_role/tasks/main.yml
- include_tasks: install_with_apt.yml
when: not install_from_source
- include_tasks: compile_and_install_from_source.yml
when: install_from_source
In the example above, the user could set install_from_source: true
or install_from_source: false
to control how the package is installed.
How to include tasks with a dynamic file name
In my experience, including tasks with a dynamic file name is most commonly used to vary tasks depending on the OS of the remote host.
Consider this example:
# ./roles/redis/tasks/main.yml
- name: install redis on Debian based distros
apt:
name: redis-server
state: present
update_cache: true
become: true
when: ansible_os_family == 'Debian'
- name: ensure epel-release repo is installed on RHEL based distros
yum:
name: epel-release
state: present
update_cache: true
become: true
when: ansible_os_family == 'RedHat'
- name: install redis on RHEL based distros
yum:
name: redis
state: present
update_cache: true
become: true
when: ansible_os_family == 'RedHat'
You could refactor these tasks into separate files and then use conditional logic to include them:
---
# ./roles/redis/tasks/setup-Debian.yml
- name: install redis on Debian based distros
apt:
name: redis-server
state: present
update_cache: true
become: true
---
# ./roles/redis/tasks/setup-RedHat.yml
- name: ensure epel-release repo is installed on RHEL based distros
yum:
name: epel-release
state: present
update_cache: true
become: true
- name: install redis on RHEL based distros
yum:
name: redis
state: present
update_cache: true
become: true
After creating the separate files, you can modify the main tasks file to look like this:
# ./roles/redis/tasks/main.yml
- include_tasks: setup-Debian.yml
when: ansible_os_family == 'Debian'
- include_tasks: setup-RedHat.yml
when: ansible_os_family == 'RedHat'
Much better, but you could make it even more terse by just using the ansible_os_family
variable to include the file in a single line:
- include_tasks: "setup-{{ ansible_os_family }}.yml"
This will produce the same results as the original list of tasks. This is a common pattern used in many third party roles you’ll find on Ansible Galaxy.
How include tasks for each item in a loop
This is probably the least common use case for include_tasks
, but I have used it a few times. This technique is useful any time you need to run multiple tasks for each item in a loop.
Here’s a contrived example to show you how you could use it: a greet_tasks.yml
list of tasks that will create a greeting fact and then print it with debug
:
---
# ./greet_tasks.yml
- name: set greeting fact
set_fact:
greeting: "Greeting {{ index }}: Hello, {{ name }}!"
- name: print greeting fact
debug: var=greeting
You can run these tasks in a loop in a playbook like this:
# ./greet.yml
- name: greet people
hosts: "*"
tasks:
- include_tasks: greet_tasks.yml
loop:
- World
- Percy
loop_control:
loop_var: name
index_var: index
Running the playbook will produce the following output:
TASK [set greeting fact]
ok: [123.123.123.123]
TASK [print greeting fact]
ok: [123.123.123.123] => {
"greeting": "Greeting 0: Hello, World!"
}
TASK [set greeting fact]
ok: [123.123.123.123]
TASK [print greeting fact]
ok: [123.123.123.123] => {
"greeting": "Greeting 1: Hello, Percy!"
}
Do you have any other use cases or examples that I didn’t cover? Hit me up in the comments and let’s discuss! 😃