Get started with Ansible Playbooks

Discover the basics of creating Ansible playbooks using practical examples, including key components such as plays, tasks, modules, and more. 

Start your Ansible Automation Platform Trial

Now that we’ve covered some of the fundamentals of writing playbooks, let’s explore more advanced topics, including handlers, conditional statements, and more.

In this lesson, you will:

  • Learn about Jinja2 templates in Ansible.
  • Learn about using handlers in playbooks.

Jinja2 templates in Ansible

Ansible uses Jinja2 templating to dynamically use variables, expressions, and control structures to customize files, playbook execution, and more based on specific conditions. You can use Jinja2 templates to:

  • Substitute variable placeholders ({{ }}) with the actual values.
  • Modify variables or include logic in configuration files.
  • Dynamically change configurations based on conditions.
  • Loop over items for a specific task.

Ansible, by default, looks for template files in the templates directory, which is in the playbooks directory:

project-name/
├── site.yml               # Playbook site.yml'
└── templates/             # Jinja2 templates folder
│   ├── index.html.j2      # index.html.j2 template

Template folder structure example.

Code

Definition

site.yml

This is the playbook.

index.html.j2

Jinja2 template file. Using the *.j2 file extension for template files is good practice.

 

Let’s use Jinja2 templating to deploy a custom index.html file to the Apache web server. First, we’ll create the index.html.j2 template in the templates folder:

# templates/index.html.j2
                                                            <!DOCTYPE html>
                                                            <html lang="en">
                                                            <head>
                                                                <meta charset="UTF-8">
                                                                <meta name="viewport" content="width=device-width, initial-scale=1.0">
                                                                <title>Ansible rocks!</title>
                                                            </head>
                                                            <body>
                                                                <h1>Server information</h1>
                                                                <p>This page is served from {{ ansible_hostname }}</p>
                                                                <p>Server IP: {{ ansible_default_ipv4.address }}</p>
                                                            </body>
                                                            </html>

Jinja2 template file example.

CodeDefinition
{{ ansible_hostname }}The ansible_hostname Ansible fact is a built-in variable that returns the managed node’s IP address or hostname.
{{ ansible_default_ipv4.address }}The ansible_default_ipv4.address Ansible fact returns the default IPv4 address of the managed node.

These variables will be substituted with values once the template is deployed to the remote node using the ansible.builtin.template module.

---
- name: Install and start Apache
  hosts: web
  become: true
  tasks:
    - name: Ensure the httpd package is installed
      ansible.builtin.package:
        name: httpd
        state: present
    - name: Deploy custom index.html
      ansible.builtin.template:
        src: index.html.j2
        dest: /var/www/html/index.html
        mode: "0644"
    - name: Start the httpd service if needed
      ansible.builtin.service:
        name: httpd
        state: started
        enabled: true

Ansible playbook Jinja2 template file example.

Code

Definition

- name: Deploy custom index.html

This task uses the ansible.builtin.template module to copy the index.html.j2 template to the target nodes.

src: index.html.j2

The name of the source template stored in the templates folder.

dest: /var/www/html/index.html

The destination path for the template.

 

Running this playbook would create a new index.html file with the following example content on the remote node:

<!DOCTYPE html>
                                                        <html lang="en">
                                                        <head>
                                                            <meta charset="UTF-8">
                                                            <meta name="viewport" content="width=device-width, initial-scale=1.0">
                                                            <title>Ansible rocks!</title>
                                                        </head>
                                                        <body>
                                                            <h1>Server information</h1>
                                                            <p>This page is served from web2.example.com</p>
                                                            <p>Server IP: 192.168.0.11</p>
                                                        </body>
                                                        </html>   

Example index.html output.

Your turn

Experiment with the examples provided and update the index.html.j2 template to show the following additional information:

  • The current date.
  • Operating System distribution name.
  • System uptime in seconds.

HINT: Refer to the Ansible facts documentation for help.

Here’s an example of an updated index.html.j2:

# templates/index.html.j2
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Ansible rocks!</title>
</head>
<body>
    <h1>Server information</h1>
    <p>This page is served from {{ ansible_hostname }}</p>
    <p>Server IP: {{ ansible_default_ipv4.address }}</p>
    <p>Current date: {{ ansible_date_time.date }}</p>
    <p>Distribution: {{ ansible_distribution }}</p>
    <p>Uptime: {{ ansible_uptime_seconds }} seconds</p>
</body>
</html>

Updated index.html.j2.

Using handlers in playbooks

Ansible uses handlers to run tasks only when a change is made on the managed node. Typically, handlers are called after a set of tasks has run, such as restarting a service if a configuration change was made.

Consider the example playbook below that installs the httpd package and updates the index.html Apache configuration file. We’ll create a handler called Restart apache and use the notify keyword to run the task only if changes are made to the file:

---
- name: Install and start Apache
  hosts: web
  become: true
  tasks:
    - name: Ensure the httpd package is installed
      ansible.builtin.package:
        name: httpd
        state: present
    - name: Start the httpd service if needed
      ansible.builtin.service:
        name: httpd
        state: started
        enabled: true
    - name: Deploy custom index.html
      ansible.builtin.template:
        src: index.html.j2
        dest: /var/www/html/index.html
        mode: "0644"
      notify:
        - Restart apache
  handlers:
    - name: Restart apache
      ansible.builtin.service:
        name: httpd
        state: restarted

Ansible playbook handler example.

Code

Definition

notify:

The notify keyword in the Deploy custom index.html task calls the Restart apache handler if the /var/www/html/index.html file changes.

handlers:

The handlers section of the playbook contains the tasks that only run when notified. In this example, the Restart apache task only executes if the Deploy custom index.html task changes the remote node’s configuration.

Ansible playbook conditionals

Conditionals enable you to control your playbook's execution flow based on variables, remote node states, or any condition Ansible can evaluate. For example, conditionals allow you to execute different playbook tasks or skip tasks based on a previous task's result.

When you add the when statement to a task, you tell Ansible to evaluate whether a conditional statement returns true or false. If the test returns true, Ansible will run the task.

Let’s illustrate a conditional statement with an example playbook to check if a website works correctly. If Apache does not return a 200 status (i.e., the website is down), the playbook returns a "Health check failed. Please investigate" message.

---
                                            - name: Install, start Apache and perform a health check
                                              hosts: web
                                              become: true
                                            
                                              tasks:
                                                - name: Perform a health check on the website
                                                  ansible.builtin.uri:
                                                    url: http://35.175.115.51/
                                                    return_content: true
                                                  register: http_response
                                                  ignore_errors: true
                                            
                                                - name: Notify if the website is not available
                                                  ansible.builtin.debug:
                                                    msg: "Health check failed. Please investigate."
                                                  when:
                                                    - http_response.status is not defined or
                                                      http_response.status != 200

Ansible playbook conditional example.

Code

Definition

register: http_response

The register keyword tells Ansible to save the task output to a variable. In this example, the Perform a health check on the website task output is saved to the http_response variable.

ignore_errors: true

The ignore_errors keyword tells Ansible to ignore any errors the task creates and continue executing the play.

when:

The this keyword tells Ansible to evaluate two conditionals and return true if one of the statements (or in the conditional statement) evaluates to true:

  • http_response.status is not defined—This statement checks to see if the http_response.status variable does not exist (is not defined).
  • http_response.status != 200—This statement checks to see if the http_response.status variable is 200.

Well done!

Congratulations on completing this learning path. The below are links to additional resources and next steps. Happy automating!

Additional resources and next steps: 

Have feedback on this learning path? Share it with us.

Previous resource
Ansible Playbook variables and facts