Bookmark this page

Including and Importing Files

Objectives

  • Manage large playbooks by importing or including other playbooks or tasks from external files, either unconditionally or based on a conditional test.

Managing Large Playbooks

When a playbook gets long or complex, you can divide it up into smaller files to make it easier to manage. You can combine multiple playbooks into a main playbook, or insert lists of tasks from a file into a play. This can make it easier to reuse plays or sequences of tasks in different projects.

Including or Importing Files

Ansible supports two operations for bringing content into a playbook. You can include content, or you can import content.

When you include content, it is a dynamic operation. Ansible processes included content during the run of the playbook, as content is reached.

When you import content, it is a static operation. Ansible preprocesses imported content when the playbook is initially parsed, before the run starts.

Importing Playbooks

The import_playbook directive allows you to import external files containing lists of plays into a playbook. In other words, you can have a master playbook that imports one or more additional playbooks.

Because the content being imported is a complete playbook, the import_playbook feature can only be used at the top level of a playbook and cannot be used inside a play. If you import multiple playbooks, then they are imported and run in order.

The following is a simple example of a master playbook that imports two additional playbooks:

- name: Prepare the web server
  import_playbook: web.yml

- name: Prepare the database server
  import_playbook: db.yml

You can also interleave plays in your master playbook with imported playbooks.

- name: Play 1
  hosts: localhost
  tasks:
    - ansible.builtin.debug:
        msg: Play 1

- name: Import Playbook
  import_playbook: play2.yml

In the preceding example, the Play 1 play runs first, followed by the plays imported from the play2.yml playbook.

Importing and Including Tasks

You can import or include a list of tasks from a task file into a play. A task file is a file that contains a flat list of tasks:

[admin@node ~]$ cat webserver_tasks.yml
- name: Installs the httpd package
  ansible.builtin.dnf:
    name: httpd
    state: latest

- name: Starts the httpd service
  ansible.builtin.service:
    name: httpd
    state: started

Importing Task Files

You can statically import a task file into a play inside a playbook by using the import_tasks feature. When you import a task file, the tasks in that file are directly inserted when the playbook is parsed. The location of import_tasks in the playbook controls where the tasks are inserted and the order in which multiple imports are run.

---
- name: Install web server
  hosts: webservers
  tasks:
  - import_tasks: webserver_tasks.yml

When you import a task file, the tasks in that file are directly inserted when the playbook is parsed. Because import_tasks statically imports the tasks when the playbook is parsed, the following items must be considered:

  • When using the import_tasks feature, conditional statements set on the import, such as when, are applied to each of the tasks that are imported.

  • You cannot use loops with the import_tasks feature.

  • If you use a variable to specify the name of the file to import, then you cannot use a host or group inventory variable.

Including Task Files

You can also dynamically include a task file into a play inside a playbook by using the include_tasks feature.

---
- name: Install web server
  hosts: webservers
  tasks:
  - include_tasks: webserver_tasks.yml

The include_tasks feature does not process content in the playbook until the play is running and that part of the play is reached. The order in which playbook content is processed impacts how the include_tasks feature works.

  • When using the include_tasks feature, conditional statements such as when set on the include determine whether the tasks are included in the play at all.

  • If you run ansible-navigator run --list-tasks to list the tasks in the playbook, then tasks in the included task files are not displayed. The tasks that include the task files are displayed. By comparison, the import_tasks feature would not list tasks that import task files, but instead would list the individual tasks from the imported task files.

  • You cannot use ansible-navigator run --start-at-task to start playbook execution from a task that is in an included task file.

  • You cannot use a notify statement to trigger a handler name that is in an included task file. You can trigger a handler in the main playbook that includes an entire task file, in which case all tasks in the included file run.

Importing and Including with Conditionals

Conditional statements behave differently depending on whether you are importing or including tasks.

  • When you add a conditional to an import_* statement, Ansible applies the condition to all tasks within the imported file.

  • When you use a conditional on an include_* statement, the condition is applied only to the include task itself and not to any other tasks within the included file.

In other words, if you put a conditional on a task that imports content, then each task in the imported content performs that conditional check before it runs.

However, if you put a conditional on a task that includes content, then the conditional determines whether the include happens or not. If the include happens, then all the tasks that are included run normally.

Note

You can find a more detailed discussion of the differences in behavior between import_tasks and include_tasks when conditionals are used at "Conditionals" in the Ansible User Guide.

Use Cases for Task Files

Consider the following examples where it might be useful to manage sets of tasks as external files separate from the playbook:

  • If new servers require complete configuration, then administrators could create various sets of tasks for creating users, installing packages, configuring services, configuring privileges, setting up access to a shared file system, hardening the servers, installing security updates, and installing a monitoring agent. Each of these sets of tasks could be managed through a separate self-contained task file.

  • If servers are managed collectively by the developers, the system administrators, and the database administrators, then every organization can write its own task file which can then be reviewed and integrated by the system manager.

  • If a server requires a particular configuration, then it can be integrated as a set of tasks that are executed based on a conditional. In other words, including the tasks only if specific criteria are met.

  • If a group of servers needs to run a particular task or set of tasks, then the tasks might only be run on a server if it is part of a specific host group.

Managing Task Files

You can create a dedicated directory for task files, and save all task files in that directory. Then your playbook can simply include or import task files from that directory. This allows construction of a complex playbook and makes it easy to manage its structure and components.

Defining Variables for External Plays and Tasks

The incorporation of plays or tasks from external files into playbooks using the Ansible import and include features greatly enhances the ability to reuse tasks and playbooks across an Ansible environment. To maximize the possibility of reuse, these task and play files should be as generic as possible. Variables can be used to parameterize play and task elements to expand the application of tasks and plays.

For example, the following task file installs the package needed for a web service, and then enables and starts the necessary service.

---
  - name: Install the httpd package
    ansible.builtin.dnf:
      name: httpd
      state: latest
  - name: Start the httpd service
    ansible.builtin.service:
      name: httpd
      enabled: true
      state: started

If you parameterize the package and service elements as shown in the following example, then the task file can also be used for the installation and administration of other software and their services, rather than being useful for a web service only.

---
  - name: Install the {{ package }} package
    ansible.builtin.dnf:
      name: "{{ package }}"
      state: latest
  - name: Start the {{ service }} service
    ansible.builtin.service:
      name: "{{ service }}"
      enabled: true
      state: started

Subsequently, when incorporating the task file into a playbook, define the variables to use for the task execution as follows:

...output omitted...
  tasks:
    - name: Import task file and set variables
      import_tasks: task.yml
      vars:
        package: httpd
        service: httpd

Ansible makes the passed variables available to the tasks imported from the external file.

You can use the same technique to make play files more reusable. When incorporating a play file into a playbook, pass the variables to use for the play execution as follows:

...output omitted...
- name: Import play file and set the variable
  import_playbook: play.yml
  vars:
    package: mariadb

Important

Earlier versions of Ansible used an include feature to include both playbooks and task files, depending on context. This functionality is being deprecated for a number of reasons.

Prior to Ansible 2.0, include operated like a static import. In Ansible 2.0 it was changed to operate dynamically, but this created some limitations. In Ansible 2.1 it became possible for include to be dynamic or static depending on task settings, which was confusing and error-prone. There were also issues with ensuring that include worked correctly in all contexts.

Thus, include was replaced in Ansible 2.4 with new directives such as include_tasks, import_tasks, and import_playbook. You might find examples of include in earlier playbooks, but you should avoid using it in new ones.

Revision: rh294-9.0-c95c7de