Abstract
| Goal |
Use Ansible Roles and Ansible Content Collections to develop playbooks more quickly and to reuse Ansible code. |
| Objectives |
|
| Sections |
|
| Lab |
|
Manage large playbooks by importing or including other playbooks or tasks from external files, either unconditionally or based on a conditional test.
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.
A file that contains only a list of tasks is called a task file. The following is an example of the content of a task file:
---
- name: Add Cisco IOS VLAN configuration
cisco.ios.ios_vlans:
config: "{{ vlans }}"
state: merged
- name: Verify Cisco IOS VLAN configuration
cisco.ios.ios_command:
commands: show vlans
register: vlan_data
- name: Display configured VLANs
ansible.builtin.debug:
var: vlan_dataSplitting a playbook into smaller files can make it easier to reuse plays or sequences of tasks in different projects.
Ansible supports two methods for bringing content into a playbook. You can include content, or you can import content.
Importing content is a static operation. Ansible preprocesses imported content when the playbook is initially parsed, before the run starts.
Including content is a dynamic operation. Ansible processes included content while the playbook is running, as content is reached.
The ansible.builtin.import_playbook module enables you to import external files containing lists of plays into a playbook.
In other words, you can have a main playbook that imports one or more additional playbooks.
If you import multiple playbooks, then they are imported and run in order.
The following is a simple example of a main playbook that imports two additional playbooks:
--- - name: Enable NETCONF serviceansible.builtin.import_playbook:juniper_netconf.yml - name: Configure SNMP settingsansible.builtin.import_playbook:juniper_snmp.yml
Notice that you must specify the imported playbook in the same line as the module.
You can also interleave plays in your main playbook with imported playbooks.
In the following example, the Back up the IOS configuration play runs first, followed by the plays imported from the ios_vlans.yml playbook.
---
- name: Back up the IOS configuration
hosts: ios
gather_facts: false
tasks:
- name: Back up the current configuration
cisco.ios.ios_config:
backup: true
backup_options:
filename: "{{ inventory_hostname }}.txt"
- name: Import IOS VLANs playbook
ansible.builtin.import_playbook: ios_vlans.ymlYou can import a task file into a play inside a playbook by using the ansible.builtin.import_tasks module.
When you import a task file, the tasks in that file are directly inserted when the playbook is parsed.
In the following example, instead of writing a list of tasks in the playbook, the task named Import Juniper Junos VLANs tasks imports the tasks from the junos_vlan.yml task file:
---
- name: Juniper Junos VLANs configuration
hosts: junos
gather_facts: false
tasks:
- name: Import Juniper Junos VLANs tasks
ansible.builtin.import_tasks:
file: junos_vlan.ymlTo import a task file, you can use the file parameter of the ansible.builtin.import_tasks module to indicate the name of the task file, or you can give the name of the task file in the same line as the module:
---
- name: Juniper Junos VLANs configuration
hosts: junos
gather_facts: false
tasks:
- name: Import Juniper Junos VLANs tasks
ansible.builtin.import_tasks: junos_vlan.ymlBecause the ansible.builtin.import_tasks module statically imports the tasks when the playbook is parsed, you must consider the following with respect to loops and conditionals:
You cannot use loops with the ansible.builtin.import_tasks module.
Conditional statements set on the import, such as when, are applied to each of the tasks that are imported.
In other words, if you use a conditional on a task that imports content, then each task in the imported content performs that conditional verification before it runs.
In the following example, all the tasks in the junos_security.yml task file run against the Juniper Junos managed nodes, and all the tasks in the ios_security.yml task file run against the Cisco IOS managed nodes:
---
- name: Apply security settings on the managed nodes
hosts: all
gather_facts: false
tasks:
- name: Import Juniper Junos security settings tasks
ansible.builtin.import_tasks: junos_security.yml
when: ansible_network_os == "junipernetworks.junos.junos"
- name: Import Cisco IOS security settings tasks
ansible.builtin.import_tasks: ios_security.yml
when: ansible_network_os == "cisco.ios.ios"You can also dynamically include a task file into a play inside a playbook by using the ansible.builtin.include_tasks module.
To include a task file, you can use the file parameter to indicate the name of the file with the task list, or you can give the name of this file in the same line as the module.
The following example uses the ansible.builtin.include_tasks module to include a task file named ios_ntp.yml in the playbook:
---
- name: Cisco IOS NTP configuration
hosts: ios
gather_facts: false
tasks:
- name: Include Cisco IOS NTP tasks
ansible.builtin.include_tasks: ios_ntp.ymlBecause the ansible.builtin.include_tasks module does not process content in the playbook until the play is running and that part of the play is reached, you must consider the following with respect to loops and conditionals:
Unlike the ansible.builtin.import_tasks module, you can use loops with the ansible.builtin.include_tasks module.
The loop applies to each task in the included content.
In the following example, the task file named ios_add_users.yml contains the list of tasks to perform to create users in the Cisco IOS managed nodes.
The list of users is given using the loop.
---
- name: Cisco IOS users
hosts: ios
gather_facts: false
tasks:
- name: Add users to Cisco IOS managed nodes
ansible.builtin.include_tasks: ios_add_users.yml
loop: "{{ expected_users }}"Conditional statements, such as when set on the ansible.builtin.include_tasks module, determine whether the tasks are included in the play at all.
The condition is applied only to the included task itself and not to any other tasks within the included file.
In other words, if you put a conditional on a task that includes content, then the conditional determines whether the content is included or not. If the condition is met, then all the tasks that are included run normally.
In the following example, if the vlans variable is defined, then the tasks in the ios_vlans.yml task file are run.
The condition is no longer verified for tasks in the ios_vlans.yml task file:
---
- name: Cisco IOS VLANs configuration
hosts: ios
gather_facts: false
tasks:
- name: Apply conditional to imported task
ansible.builtin.include_tasks: ios_vlans.yml
when: vlans is definedConsider the following examples where it might be useful to manage sets of tasks as external files separate from the playbook:
If new managed nodes require complete configuration, then network administrators could create various sets of tasks for creating users and setting up their access, configuring the login banner on the managed nodes, hardening the managed nodes, installing security updates, configuring DNS, Simple Network Management Protocol (SNMP), logging, VLANs, NTP settings, and so on. Each of these sets of tasks could be managed through a separate self-contained task file.
If different groups of network administrators are in charge of the managed nodes, then each group can write their own task file, which can then be reviewed and integrated by the network manager.
If a managed node 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 managed nodes needs to run a particular task or set of tasks, then the tasks might only be run on a managed node if it is part of that specific group.
The incorporation of plays or tasks from external files into playbooks using the Ansible import and include features enhances the ability to reuse tasks and playbooks across an Ansible environment.
You can create a dedicated directory for task files, and save all task files in that directory. Your playbook can then include or import task files from that directory.
For example, suppose that you have the following task file named ios_verification.yml that verifies the NTP configuration in the Cisco IOS managed nodes, and then displays the existing NTP configuration:
---
- name: Verifying NTP configuration
cisco.ios.ios_command:
commands:
- show run | include ntp
register: ios_value
- name: Display NTP configuration
ansible.builtin.debug:
var: ios_value['stdout_lines']If the previous task file was saved in a tasks directory, then you might incorporate the tasks as follows:
...output omitted... - name: Import task fileansible.builtin.import_tasks:tasks/ios_verification.yml ...output omitted...
This enables you to construct a complex playbook and makes it easy to manage its structure and components.
To maximize the possibility of reuse, plays and tasks should be as generic as possible. Use variables to parameterize play and task elements to expand the application of plays and tasks.
For example, if you parameterize the configuration to be verified from the previous example, then the ios_verification.yml task file can also be used for the verification of other configurations, rather than being useful only for verifying the NTP configuration:
- name: Verifying{{ ios_service }}configuration cisco.ios.ios_command: commands: "{{ ios_commands }}" register: ios_value - name: Display{{ ios_service }}configuration ansible.builtin.debug: var: ios_value['stdout_lines']
When incorporating the task file in your playbook, you can define the variables to use for the task execution as follows:
...output omitted... - name: Import task file and set variablesansible.builtin.import_tasks: tasks/ios_verification.ymlvars:ios_service: NTPios_commands: - show run | include ntp ...output omitted...
The following example shows how to use the same task file to verify DNS on the managed nodes:
...output omitted... - name: Import task file and set variablesansible.builtin.import_tasks: tasks/ios_verification.ymlvars:ios_service: DNSios_commands: - show ip name-server - show ip dns view | include list ...output omitted...
You can use the same technique to make playbooks more reusable. When incorporating a playbook into another playbook, pass the variables to use for the play execution as follows:
- name: Import play file and set the variableansible.builtin.import_playbook: play.ymlvars: managed_nodes: junos ...output omitted...
Re-using Ansible Artifacts - Ansible Documentation
ansible.builtin.import_playbook Module - Import a Playbook - Ansible Documentation
ansible.builtin.import_tasks Module - Import a Task List - Ansible Documentation
ansible.builtin.include_tasks Module - Dynamically Include a Task List - Ansible Documentation