When you create an Ansible playbook for larger projects, putting all the tasks in a single playbook will make the project complex. As a best practice, you have to group the task into separate playbooks. This will help you break down your project and achieve modularity in Ansible. In ansible, you can break the playbook, and task and import them using the "import_*
" and "include_*
" directives. In this article, we will learn how to work with include
and import
statements in Ansible with examples.
In the following sections, I will demonstrate how to import playbooks, tasks, and roles with simple examples. If you need an ansible lab to practice, please take a look at our ansible lab setup guide using Vagrant and Virtualbox in the link below.
Automated Ansible Lab Setup With Vagrant And Virtualbox In Linux
Table of Contents
Includes vs Imports
As stated in the Ansible official documentation,
the
include
andimport
statements are very similar, however the Ansible executor engine treats them very differently.
- All
import*
statements are pre-processed at the time playbooks are parsed.- All
include*
statements are processed as they are encountered during the execution of the playbook.
The "import_playbook
" directive is introduced in ansible version 2.4 and prior to that only include_playbook
was available. Post version 2.4 only the import_playbook
directive is available and If you try to use the include_playbook
directive in older versions, you will get the following error.
ERROR! 'include_playbook' is not a valid attribute for a Play
Importing Ansible Playbook
You can create multiple playbooks according to the nature of the play and finally import them under a single YAML file and run the playbook.
I created three playbooks.
- First playbook -
playbook1.yml
- Second playbook -
playbook2.yml
- Main playbook -
main.yml
The first and second playbook contains the following play definition.
# playbook1.yml - name: Playbook imports hosts: localhost connection: local gather_facts: False tasks: - name: Task Import ansible.builtin.debug: msg: "Importing playbook1.yml" # playbook2.yml - name: Playbook imports hosts: localhost connection: local gather_facts: False tasks: - name: Task Import 2 ansible.builtin.debug: msg: "Importing playbook2.yml"
The main playbook is used to import both playbook1.yml
and playbook2.yml
.
- import_playbook: playbook1.yml - import_playbook: playbook2.yml
Running the ansible-playbook
command against the main.yml
will import playbook1.yml
and playbook2.yml
playbooks into the current namespace and execute the tasks.
$ ansible-playbook main.yml PLAY [Playbook imports] ********************************************************************************** TASK [Task Import] *************************************************************************************** ok: [localhost] => { "msg": "Importing playbook1.yml" } PLAY [Playbook imports] ********************************************************************************** TASK [Task Import 2] ************************************************************************************* ok: [localhost] => { "msg": "Importing playbook2.yml" }
Applying Other Directives with import_playbook
You can apply other directives like tags, conditional statement etc., with the import_playbook
directive. The directive at the import level will be inherited by all the playbook tasks.
To understand it better, consider the below example. All I did was simply added a conditional statement to the main.yml
file.
- import_playbook: playbook1.yml when: run is defined - import_playbook: playbook2.yml when: run is defined
When I run this playbook, the plays, and the task is imported into the namespace and checked if the variable "run
" is defined against all tasks. Since the variable is not defined the tasks are skipped.
PLAY [Playbook imports] ********************************************************************************** TASK [Task Import] *************************************************************************************** skipping: [localhost] PLAY [Playbook imports] ********************************************************************************** TASK [Task Import 2] ************************************************************************************* skipping: [localhost]
I am running the playbook again by setting var=1
in playbook2 alone. This time the task under playbook2 ran fine and playbook1 tasks were skipped.
PLAY [Playbook imports] ******************************************************************************************************** TASK [Task Import] ************************************************************************************************************* skipping: [localhost] PLAY [Playbook imports] ******************************************************************************************************** TASK [Task Import 2] *********************************************************************************************************** ok: [localhost] => { "msg": "Importing playbook2.yml" } PLAY RECAP ********************************************************************************************************************* localhost : ok=1 changed=0 unreachable=0 failed=0 skipped=1 rescued=0 ignored=0
Importing Ansible Playbook Tasks
Similar to importing playbooks, you can create separate task files and import them into a single file. While playbooks can only use import directive, tasks can use both import and include directive.
First, let’s take a look at the import_task
directive which is similar to the import_playbook
directive. The directive will pre-parse the task when the playbook is parsed.
I have created two files named task1.yml
and main.yml
. It contains the following play and task definitions.
# task1.yml - name: importing_tasks ansible.builtin.debug: msg: "Importing the task from task1.yml" # main.yml --- - hosts: localhost connection: local gather_facts: false tasks: - import_tasks: task1.yml
If you take a look at the above output, the task will be imported into the main.yml
namespace and run.
The include_tasks
directive will be parsed only at the time of playbook execution. I am using the same main.yml
and task1.yml
playbook but with little modifications.
# main.yml --- - hosts: localhost connection: local gather_facts: false tasks: - include_tasks: task1.yml # task1.yml - name: importing tasks using include directive ansible.builtin.debug: msg: "Importing the task from task1.yml using include directive"
When the playbook is submitted, two tasks ran, even though we have only one task defined in the playbook. The first task is for parsing the tasks from task1.yml
and include them for execution. The second task executes the included tasks.
Similar to the import_playbook
directive, you can apply other ansible directives to the import_tasks
or include_tasks
directive.
I have created a task2.yml
file and used the block directive to import and include task1.yml
and task2.yml
.
# task2.yml - name: importing tasks using include directive ansible.builtin.debug: msg: "Importing the task from task2.yml using import directive" # main.yml --- - hosts: localhost connection: local gather_facts: false tasks: - name: Import and include with block directive block: - include_tasks: task1.yml - import_tasks: task2.yml
Importing Ansible Playbook Roles
Roles are a great way to organize your project. We have a detailed article about ansible roles and how import and include directives are used as an alternative to roles directives. I suggest you take a look at it in the link below.
Conclusion
In this article we have seen what import_*
and include_*
directives are and how to use them to split your ansible project to achieve modularity.