Ansible Playbooks

In comparison with ad-hoc commands, playbooks are used in complex scenarios, and they offer increased flexibility. Playbooks use YAML format, so there is not much syntax needed, but indentation must be respected. Ansible playbooks tend to be more of a configuration language than a programming language.

Like the name is saying, a playbook is a collection of plays. Through a playbook, you can designate specific roles to some of the hosts and other roles to other hosts. By doing so, you can orchestrate multiple servers in very diverse scenarios, all in one playbook.

 

Blocks
Complex playbooks may contain a long list of tasks. Some tasks in the list may be related in their function. With Ansible version 2.0, blocks offer another alternative to task organization. Blocks can be used to group related tasks together. This not only improves readability but also allows task parameters to be performed on a block level when writing more complex playbooks. The following examples show how a list of tasks can be organized into distinct groups with the use of blocks.

 

 

Multiple plays
Playbook can contain one or more plays. Because plays map managed hosts to tasks, scenarios that require different tasks to be performed on different hosts, such as orchestration, necessitate the use of different plays. Rather than having plays in separate playbook files, multiple plays can be placed in the same playbook file. Plays are expressed in a list context, so the start of each play is indicated by a preceding dash and space.

The following example shows the format for creating a simple playbook with multiple plays.

 

 

To have all the details precise before continuing with examples, we must first define a task. These are the interface to ansible modules for roles and playbooks.

One playbook with one play, containing multiple tasks looks like this.

Explanation:

  • hosts – Group of hosts on which the playbook will run
  • Yum module is used in this task for lldpad installation
  • The service module is used to check if the service is up and running after installation

The ordering of the contents within a playbook is important, because Ansible executes plays and tasks in the order they are presented.

Each ansible playbook works with an inventory file. The inventory file contains a list of servers divided into groups for better control for details like IP address and SSH port for each host.

The inventory file you can use for this example looks like below. There are two groups, named group1 and group2 each containing host1 and host2 respectively.

 

Another useful example of an Ansible playbook containing this time two plays for two hosts is the next one. For the first group of hosts, group1, selinux will be enabled. If it is enabled, then a message will appear on the screen of the host.

For the second group of hosts, httpd package will be installed only if the ansible_os_family is RedHat and ansible_system_vendor is HP.

Ansible_os_family and ansible_system_vendor are variables gathered with gather_facts option and can be used like in this conditional example.

Explanation:

Example of the when clause, In this case, when OS type is Debian. The ansible_os_family variable is gathered via gather_facts functionality.
The task output is registered for future use, with its name enable_selinux
Another example of the when clause. In this case, a message will be displayed for the host user if the SELinux was indeed enabled before. Another example of the when clause consisting of two rules.

 

Besides tasks, there are also some particular tasks called handlers. Handlers must have a unique name throughout the playbook. These work in the same way as a regular task but a handler can be notified via a notifier.

If a handler is not notified during the run of the playbook, it will not run. However, if more than one task notifies a handler, this will run only once after all the tasks are finished.

In the example shown below, you can see how a specific task has a notify section which calls upon another task. If the output of the first task is changed, then a handler task will be called. The best example is to have a configuration file changed and afterward restart that specific service.

In this case, if the first task, "sshd config file modify port" is changed, meaning that if the port is not 28675 in the first place, then it will be modified and the task will notify the handler with the same name to run, and it will restart the sshd service.

 

Running playbooks Basic Playbook Syntax

The playbook will install apache in the newest version.

Modification playbook to start and enable httpd service:

Adding index.html file:

We can limit running playbook only on one host. It is usefull when you engineering playbooks.

Let’s check if our playbook works:

 

Use Variables to Retrieve the Results of Running Commands

  • The register keyword
  • May be referenced within the play
  • Many attributes returned

Example

 

Let’s write uid of output to the file:

 

Executing a dry run
Another helpful option is the -C option. This causes Ansible to report what changes would have occurred if the playbook were executed, but does not make any actual changes to managed hosts. The following example shows the dry run of a playbook containing a single task for ensuring that the latest version of httpd package is installed on a managed host. Note that the dry run reports that the task would effect a change on the managed host.

 

Step-by-step execution
When developing new playbooks, it may be helpful to execute the playbook interactively. The ansible-playbook command offers the --step option for this purpose. When executed with this option, Ansible steps through each task in the playbook. Prior to executing each task, it prompts the user for input. User can choose ‘y’ to execute the task, 'n' to skip the task, or 'c' to exit step-by-step execution and execute the remaining tasks noninteractively.
The following example shows the step-by-step run of a playbook containing a task for ensuring that the latest version of the httpd package is installed on a managed host.

 

Idempotency

When possible, try to avoid the command, shell, and raw modules in playbooks. Because these take arbitrary commands, it is very easy to write non-idempotent playbooks with these modules. For example, this task using the shell module is not idempotent. Every time the play is run, it will rewrite /etc/resolv.conf even if it already consists of the line "nameserver 192.0.2.1".

A number of things could be done to use the shell module in an idempotent way, and sometimes making those changes and using shell is the best approach. But a quicker solution may be to use copy module and use that to get the desired effect. The following example will not rewrite the file /etc/resolv.conf if it already consists of the right content:

The copy module is special-purpose and can easily test to see if the state has already been met, and if it has will make no changes. The shell module allows a lot of flexibility, but also requires more attention to ensure that it runs in an idempotent way. Idempotent playbooks can be run repeatedly to ensure systems are in a particular state without disrupting those systems if they already are.

 

Example 1.

This demonstration illustrates the creation and use of Ansible playbooks. The demonstration will be conducted out of the /home/student/imp-playdemo directory on workstation using the supplied configuration file, inventory, and playbooks. It is assumed that workstation is the control node, and that servera.lab.example.com is a managed host. The control node
also manages itself as a managed host. The managed hosts have a devops user that is able to get root access through sudo without a password. SSH authentication keys are set up to give student login access to the devops user.
The supplied configuration file specifies the remote user and the location of the inventory. It also configures Ansible logging to be enabled on the control node and managed hosts.

1. Log in as the student user on workstation. Change directory to the working directory and review the contents of the directory.

2. Review the /home/student/imp-playdemo/ftpclient.yml playbook. It ensures that the latest version of the lftp package is installed on the local managed host. This operation will require escalated privileges, so the playbook enables privilege escalation using sudo and the root user. The connection to the managed host is made with the devops user.


3. Review the/home/student/imp-playdemo/ftpserver.yml playbook. It ensures that the latest version of the vsftpd and firewalld packages are installed on servera. This operation will require escalated privileges, so the playbook enables privilege escalation using sudo and the root user. The connection to the managed host is made with the devops user. The playbook ensures that firewalld allows incoming connections for the FTP service. It
also ensures that the vsftpd and firewalld services are enabled and running.
Related tasks are grouped together using blocks.

4. Execute the /home/student/imp-playdemo/ftpclient.yml playbook.

 

 

Example 2.

A developer has asked you to configure Ansible to automate the setup of web servers for your company’s intranet web site. The developer is using the host servera to develop the web site and test your Ansible playbook.
A working directory, /home/student/imp-playbook, has been created on workstation for the purpose of managing the managed node, servera. The directory has already been populated with an ansible.cfg configuration file and an inventory inventory file. The managed host, servera, is already defined in this inventory file. The developer needs the managed host to have the latest versions of the httpd and firewalld packages installed. Also, the httpd and firewalld services need to be enabled and running. Lastly, firewalld should allow remote systems access to the HTTP service.

Construct a playbook on the control node, workstation, called /home/student/impplaybook/intranet.yml. Create a play in this playbook that configures the managed host as requested by the developer. Also include a task to create the /var/www/html/index.html file to test the installation. Populate this file with the message ‘Welcome to the example.com intranet! ‘.

The playbook should also contain another play which performs a test from the control node to ensure that the web server is accessible across the network. This play should be comprised of a task which makes an HTTP request to http://servera.lab.example.com/index.html and verifies that the HTTP status return code is 200.

Final playbook intranet.yml looks like this:

 

Example.

A developer responsible for the company’s Internet website has asked you to write an Ansible playbook to automate the setup of his server environment on serverb.lab.example.com. A working directory, /home/student/imp-lab, has been created on workstation for the purpose of managing the managed node, serverb. The directory has already been populated with an ansible.cfg configuration file and an inventory inventory file. The managed host, serverb, is already defined in this inventory file. The application being developed on serverb will require the installation of the latest versions of the firewalld, httpd, php, php-mysql, and mariadb-server packages. The firewalld, httpd, and mariadb services also need to be enabled and running. The firewalld service must also provide access for remote systems to the web site provided by the httpd service.

Construct a playbook on the control node, workstation, called internet.yml. Create a play in this playbook that configures the managed host as requested by the developer. Place the package installation tasks within a block. Place the firewall configuration task within a separate block. Place the service management tasks within another separate block. In a final block, include a task that uses the get_url module to fetch and populate the content for the /var/www/html/index.php file on the managed host to test the installation. This content is available for download at http://materials.example.com/ grading/var/www/html/index.php.

The playbook should also contain another play which performs a simple test from the control node to ensure that the web server is accessible across the network as expected. This play should be comprised of a task which uses the uri module to make an HTTP request to http:// serverb.lab.example.com/index.php and verifies that the HTTP status return code is 200.