When working with complex or long playbooks, administrators can use separate files to divide tasks and lists of variables into smaller pieces for easier management. There are multiple ways to include task files and variables in a playbook.
• Tasks can be included in a playbook from an external file by using the include
directive.
1 2 3 |
tasks: - name: Include tasks to install the database server include: tasks/db_server.yml |
• The include_vars
module can include variables defined in either JSON or YAML files, overriding host variables and playbook variables already defined.
1 2 3 |
tasks: - name: Include the variables from a YAML or JSON file include_vars: vars/variables.yml |
Using multiple, external files for tasks and variables is a convenient way to build the main playbook in a modular way, and facilitates reuse of Ansible elements across multiple playbooks.
Including tasks
The include directive allows administrators to have a task file inserted at a particular point in a playbook. A task file is simply a file that contains a flat list of tasks ( environment.yml
):
1 2 3 4 5 6 7 8 |
- name: Installs the {{ package }} package yum: name: "{{ package }}" state: latest - name: Starts the {{ service }} service service: name: "{{ service }}" state: "{{ state }}" |
An include
directive with variables could be used in a playbook, like this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
--- - name: Install, start, and enable services hosts: all tasks: - name: Includes the tasks file and defines the variables include: environment.yml vars: package: mariadb-server service: mariadb state: started register: output - name: Debugs the included tasks debug: var: output |
Important
Tasks included in a playbook are evaluated based on their order in the playbook. For example, a playbook may have a task to start a service provided by a software package, and a second task included from an external file that installs that software package. The external task must be included in the playbook before the task to start the package’s service is declared in the playbook.
For complex projects, roles provide a powerful way to organize included task files and playbooks.
Including variables
Just as tasks can be included in playbooks, variables can be externally defined and included in playbooks. There are many ways in which this can be done. Some of the ways to set variables include:
• Inventory variables defined in the inventory file or in external files in host_vars
and group_vars
directories
• Facts and registered
variables
• Playbook variables defined in the playbook file with vars or in an external file through vars_files
The following YAML file, variables.yml
, contains two variables
that define the packages to install:
1 2 3 4 |
--- packages: web_package: httpd db_package: mariadb-server |
To import these two variables in a playbook, the include_vars
module can be used:
1 2 3 4 5 6 7 8 9 10 |
--- - name: Install web application packages hosts: all tasks: - name: Includes the tasks file and defines the variables include_vars: variables.yml - name: Debugs the variables imported debug: msg: > "{{ packages['web_package'] }} and {{ packages.db_package }} have been imported" |
Important
When deciding where to define variables, try to keep things simple.
In most cases, it is not necessary to use include_vars, host_vars and
group_vars files, playbook vars_files directives, inlined variables in playbook and inventory files, and command-line overrides all at the same time. In fact, that much complexity could make it hard to easily maintain your Ansible project.
Example 1.
Define the paths.yml
variables file and create a dictionary that sets some system paths. Specify the fileserver base path with the ansible_fqdn
variable and dbpath base path with the ansible_fqdn
variable.
1 2 3 4 |
--- paths: fileserver: /home/student/srv/filer/{{ ansible_fqdn }} dbpath: /home/student/srv/database/{{ ansible_fqdn }} |
Create the fileservers.yml playbook and include the paths.yml variables file. The fileserver will be created using the variable defined previously in the paths.yml variables file.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
--- - name: Configure fileservers hosts: fileservers tasks: - name: Imports the variables file include_vars: paths.yml - name: Creates the remote directory file: path: "{{ paths.fileserver }}" state: directory mode: 0755 register: result - name: Debugs the results debug: var: result |
Run the fileservers.yml playbook and examine the output.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
[student@workstation demo-vars-inclusions]$ ansible-playbook fileservers.yml PLAY [Configure fileservers] *********************************************** TASK [setup] *************************************************************** Chapter 4. Managing Variables and Inclusions 144 DO407-A2.0-en-1-20160804 ok: [servera.lab.example.com] TASK [Imports the variables file] ****************************************** ok: [servera.lab.example.com] TASK [Creates the remote directory] **************************************** changed: [servera.lab.example.com] TASK [Debugs the results] ************************************************** ok: [servera.lab.example.com] => { "result": { "changed": true, "gid": 0, "group": "root", "mode": "0755", "owner": "root", "path": "/home/student/srv/filer/servera.lab.example.com", "secontext": "unconfined_u:object_r:user_home_t:s0", "size": 6, "state": "directory", "uid": 0 } } PLAY RECAP ***************************************************************** servera.lab.example.com : ok=4 changed=1 unreachable=0 failed=0 |
The output shows the directory structure that has been created by Ansible, which matches the path that has been set by the paths.fileserver variable.
Create the dbservers.yml
playbook. Include the variable file and use the paths.dbpath
variable to create the directory structure.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
--- - name: Configure DB Servers hosts: dbservers tasks: - name: Imports the variables file include_vars: paths.yml - name: Creates the remote directory file: path: "{{ paths.dbpath }}" state: directory mode: 0755 register: result - name: Debugs the results debug: var: result |
Run the dbservers.yml
playbook and examine the output.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
[student@workstation demo-vars-inclusions]$ ansible-playbook dbservers.yml PLAY [Configure DB Servers] ************************************************ TASK [setup] *************************************************************** ok: [servera.lab.example.com] TASK [Imports the variables file] ****************************************** Including variables DO407-A2.0-en-1-20160804 145 ok: [servera.lab.example.com] TASK [Creates the remote directory] **************************************** changed: [servera.lab.example.com] TASK [Debugs the results] ************************************************** ok: [servera.lab.example.com] => { "result": { "changed": true, "gid": 0, "group": "root", "mode": "0755", "owner": "root", "path": "/home/student/srv/database/servera.lab.example.com", "secontext": "unconfined_u:object_r:user_home_t:s0", "size": 6, "state": "directory", "uid": 0 } } PLAY RECAP ***************************************************************** servera.lab.example.com : ok=4 changed=1 unreachable=0 failed=0 |
Create another variable file, package.yml
, and define the name of the package to install.
1 2 3 |
--- packages: web_pkg: httpd |
Create a task file, called install_package.yml
, and create a basic task that installs a package.
1 2 3 4 5 |
--- - name: Installs {{ packages.web_pkg }} yum: name: "{{ packages.web_pkg }}" state: latest |
Create the playbook.yml
playbook. Define it for the hosts in the fileservers group. Include both variable files as well as the task file.
1 2 3 4 5 6 7 8 |
--- - name: Install fileserver packages hosts: fileservers tasks: - name: Includes the variable include_vars: package.yml - name: Installs the package include: install_package.yml |
Run the playbook and watch the output.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
[student@workstation demo-vars-inclusions]$ ansible-playbook playbook.yml PLAY [Install fileserver packages] ***************************************** Chapter 4. Managing Variables and Inclusions 146 DO407-A2.0-en-1-20160804 TASK [setup] *************************************************************** ok: [servera.lab.example.com] TASK [Includes the variable] *********************************************** ok: [servera.lab.example.com] TASK [Installs the package] ************************************************ included: /home/student/demo-vars-inclusions/install_package.yml for servera.lab.example.com TASK [Installs httpd] ****************************************************** changed: [servera.lab.example.com] PLAY RECAP ***************************************************************** servera.lab.example.com : ok=4 changed=1 unreachable=0 failed=0 |
Note the httpd package has been installed from a task, whereas the name of the package to install is defined in the variables file.
Update the playbook.yml
playbook to override the name of the package to install. Append a vars block to the include statement and define a dictionary to override the name of the package to install.
1 2 3 4 5 6 7 8 9 10 11 |
--- - name: Install fileserver packages hosts: fileservers tasks: - name: Includes the variable include_vars: package.yml - name: Installs the package include: install_package.yml vars: packages: web_pkg: tomcat |
Run the playbook and watch the output as Ansible installs the tomcat package.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
[student@workstation demo-vars-inclusions]$ ansible-playbook playbook.yml PLAY [Install fileserver packages] ***************************************** TASK [setup] *************************************************************** ok: [servera.lab.example.com] TASK [Includes the variable] *********************************************** ok: [servera.lab.example.com] TASK [Installs the package] ************************************************ included: /home/student/demo-vars-inclusions/install_package.yml for servera.lab.example.com TASK [Installs tomcat] ***************************************************** changed: [servera.lab.example.com] PLAY RECAP ***************************************************************** servera.lab.example.com : ok=4 changed=1 unreachable=0 failed=0 |
Example 2.
In the vars directory, create the variables.yml
variables file. The file defines the firewall_pkg
variable in YAML format. The file should read as follows:
1 2 |
--- firewall_pkg: firewalld |
In the tasks directory, create the environment.yml
task file. Define the two tasks that install and start the web server; use the package variable for the package name, service for the service name, and svc_state for the service state.
1 2 3 4 5 6 7 8 9 |
--- - name: Install the {{ package }} package yum: name: "{{ package }}" state: latest - name: Start the {{ service }} service service: name: "{{ service }}" state: "{{ svc_state }}" |
Create and edit the main playbook, named playbook.yml. The playbook imports the tasks as well as the variables; and it installs the firewalld service and configures it. Start by adding the webserver host group. Define a rule variable with a value of http.
Define the first task, which uses the include_vars
module to import extra variables in the playbook. The variables are used by other tasks in the playbook. Include the variables.yml
variable file created previously.
Define the second task which uses the include module to include the base
environment.yml
playbook. Because the three defined variables are used in the base playbook, but are not defined, include a vars block. Set three variables in the vars section: package
set as httpd, service
set as httpd, and svc_state
set as started.
Create three more tasks: one that installs the firewalld package, one that starts the firewalld service, and one that adds a rule for the HTTP service. Use the variables that were defined previously.
Create a task that installs the firewalld package using the firewall_pkg
variable. Create a task that starts the firewalld service. Create a task that adds a firewall rule for the HTTP service using the rule variable.
Finally, add a task that creates the index.html
file for the web server using the copy module. Create the file with the Ansible ansible_fqdn fact
, which returns the fully qualified domain name. Also include a time stamp in the file using an Ansible fact.
The main playbook.yml
playbook appear as follows:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
--- - hosts: webserver vars: rule: http tasks: - name: Include the variables from the YAML file include_vars: vars/variables.yml - name: Include the environment file and set the variables include: tasks/environment.yml vars: package: httpd service: httpd svc_state: started - name: Install the firewall yum: name: "{{ firewall_pkg }}" state: latest - name: Start the firewall service: name: firewalld state: started enabled: true - name: Open the port for {{ rule }} firewalld: service: "{{ rule }}" immediate: true permanent: true state: enabled - name: Create index.html copy: content: "{{ ansible_fqdn }} has been customized using Ansible on the{{ ansible_date_time.date }}\n" dest: /var/www/html/index.html |
Run the playbook using the ansible-playbook command. Watch the output as Ansible starts by including the environment.yml playbook and running its tasks, then keeps executing the tasks defined in the main playbook.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
[student@workstation dev-vars-inclusions]$ ansible-playbook playbook.yml PLAY *********************************************************************** TASK [setup] *************************************************************** ok: [servera.lab.example.com] TASK [Include the variables from the YAML file] **************************** ok: [servera.lab.example.com] TASK [Include the environment file and set the variables] ****************** included: /home/student/dev-vars-inclusions/tasks/environment.yml for servera.lab.example.com TASK [Install and start the web server] ************************************ changed: [servera.lab.example.com] TASK [Start the service] *************************************************** changed: [servera.lab.example.com] TASK [Install the firewall] ************************************************ changed: [servera.lab.example.com] TASK [Start the firewall] ************************************************** changed: [servera.lab.example.com] TASK [Open the port for http] ********************************************** changed: [servera.lab.example.com] TASK [Create index.html] *************************************************** changed: [servera.lab.example.com] PLAY RECAP ***************************************************************** servera.lab.example.com : ok=9 changed=4 unreachable=0 failed=0 |
Use curl to ensure the web server is reachable from workstation. Because the
index.html
has been created, the output should appear as follows:
1 2 |
[student@workstation dev-vars-inclusions]$ curl http://servera.lab.example.com servera.lab.example.com has been customized using Ansible on the 2016-03-31 |
Example 2.
Create a facts file in INI format called custom.fact. Create a section called packages
and define two facts: one called db_package
, with a value of mariadb-server
, and one called web_package
, with a value of httpd
. Create a section called services with two facts: one called db_service
, with a value of mariadb
, and one called web_service
, with a value of httpd
.
Define a playbook, called setup_facts.yml, that will install the facts on serverb.
Create the fact file custom.fact
. The file should appear as follows:
1 2 3 4 5 6 7 |
[packages] db_package = mariadb-server web_package = httpd [services] db_service = mariadb web_service = httpd |
From the project directory, create the setup_facts.yml
playbook to install the facts on the managed host, serverb.lab.example.com. Use the file and copy modules to install the custom facts. The playbook should appear as follows:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
--- - name: Install remote facts hosts: lamp vars: remote_dir: /etc/ansible/facts.d facts_file: custom.fact tasks: - name: Create the remote directory file: state: directory recurse: yes path: "{{ remote_dir }}" - name: Install the new facts copy: src: "{{ facts_file }}" dest: "{{ remote_dir }}" |
Run the playbook to install the custom facts and verify the facts are available as Ansible facts.
1 2 3 4 5 6 7 8 9 10 |
[student@workstation lab-managing-vars]$ ansible-playbook setup_facts.yml PLAY [Install remote facts] ************************************************ TASK [setup] *************************************************************** changed: [serverb.lab.example.com] TASK [Create the remote directory] ***************************************** ok: [serverb.lab.example.com] TASK [Install the new facts] *********************************************** changed: [serverb.lab.example.com] PLAY RECAP ***************************************************************** serverb.lab.example.com : ok=3 changed=2 unreachable=0 failed=0 |
Ensure the newly created facts can be retrieved.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
[student@workstation lab-managing-vars]$ ansible lamp -m setup -a 'filter=ansible_local*' serverb.lab.example.com | SUCCESS => { "ansible_facts": { "ansible_local": { "custom": { "packages": { "db_package": "mariadb-server", "web_package": "httpd" }, "services": { "db_service": "mariadb", "web_service": "httpd" } } } }, "changed": false } |
Create a directory for variables, called vars. Define a YAML variable file, called main.yml, in that directory that defines a new variable, called web_root, with a value of /var/www/html.
Create the variables directory, vars, inside the project directory.
1 |
[student@workstation lab-managing-vars]$ mkdir vars |
Create the variables file vars/main.yml
. The file should contain the following content:
1 2 |
--- web_root: /var/www/html |
From the project directory, lab-managing-vars, create a directory for tasks, called tasks. Define a task file in the subdirectory, called main.yml
, that instructs Ansible to install both the web server package and the database package using facts Ansible gathered from serverb.lab.example.com. When they are installed, start the two services.
Create the tasks directory inside the project directory.
1 |
[student@workstation lab-managing-vars]$ mkdir tasks |
Create the tasks file, tasks/main.yml
. The tasks should install both the database and the web server, and start the services httpd and mariadb. Use the custom Ansible facts for the name of the services to manage. The file should appear as follows:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
--- - name: Install and start the database and web servers yum: name: - "{{ ansible_local.custom.packages.db_package }}" - "{{ ansible_local.custom.packages.web_package }}" state: latest - name: Start the database service service: name: "{{ ansible_local.custom.services.db_service }}" state: started enabled: true - name: Start the web service service: name: "{{ ansible_local.custom.services.web_service }}" state: started enabled: true |
Create the main playbook playbook.yml
in the top-level directory for this lab. The playbook should be in the following order: target the lamp hosts groups and define a new variable, firewall with a value of firewalld.
Create the following tasks:
• A task that includes the variable file main.yml
.
• A task that includes the tasks defined in the tasks file.
• A task for installing the latest version of the firewall package.
• A task for for starting the firewall service.
• A task for opening TCP port 80 permanently.
• A task that uses the copy module to create the index.html
page in the directory the variable defines.
The index.html should appear as follows:
1 |
serverb.lab.example.com (172.25.250.11) has been customized by Ansible |
Both the host name and the IP address should use Ansible facts.
When complete, the tree should appear as follows:
1 2 3 4 5 6 7 8 9 10 11 12 |
[student@workstation lab-managing-vars]$ tree . ├── ansible.cfg ├── custom.fact ├── inventory ├── playbook.yml ├── setup_facts.yml ├── tasks │ └── main.yml └── vars └── main.yml 2 directories, 7 files |
The main playbook should appear as follows:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
--- - hosts: lamp vars: firewall: firewalld tasks: - name: Include the variable file include_vars: vars/main.yml - name: Include the tasks include: tasks/main.yml - name: Install the firewall yum: name: "{{ firewall }}" state: latest - name: Start the firewall service: name: "{{ firewall }}" state: started enabled: true - name: Open the port for the web server firewalld: service: http state: enabled immediate: true permanent: true - name: Create index.html copy: content: "{{ ansible_fqdn }}({{ ansible_default_ipv4.address }}) has been customized by Ansible\n" dest: "{{ web_root }}/index.html" |
Explanation:
First task includes the variables file located under vars/main.yml. Second task imports the tasks file located under tasks/main.yml. Next tasks that install the firewall, start the service, open port 80, and reload the service. Finally, create the task that uses the copy module to create a custom main page, index.html. Use the variable web_root
, defined in the variables file, for the home directory of the web server.
Run the playbook playbook.yml
created in the previous step.
1 2 3 4 5 |
[student@workstation lab-managing-vars]$ ansible-playbook playbook.yml PLAY *********************************************************************** ... Output omitted ... PLAY RECAP ***************************************************************** serverb.lab.example.com : ok=10 changed=5 unreachable=0 failed=0 |
From workstation, use curl to ensure the web server is reachable.
1 2 |
[student@workstation lab-managing-vars]$ curl http://serverb serverb.lab.example.com(172.25.250.11) has been customized by Ansible |
Also use an ad hoc command to connect to serverb as the devops user and ensure the mariadb service is running.
1 2 3 4 5 6 7 8 |
[student@workstation lab-managing-vars]$ ansible lamp -a 'systemctl status mariadb' serverb.lab.example.com | SUCCESS | rc=0 >> ● mariadb.service - MariaDB database server Loaded: loaded (/usr/lib/systemd/system/mariadb.service; disabled; vendor preset: disabled) Active: active (running) since Fri 2016-04-01 10:50:40 PDT; 7min ago ... Output omitted ... |