Ansible Playbook Examples: A Beginners Guide

Ansible Playbooks Example

Ansible Playbooks are the heart of Ansible automation. They provide a simple way to define automation workflows using YAML. Playbooks consist of instructions (tasks) that Ansible executes on remote machines. They are easy to read and manage, making them ideal for automating IT processes such as configuration management, software deployment, and task execution.

In this guide, you’ll learn how to create, manage, and optimize Ansible Playbooks with examples.

Understanding the Basics of Ansible Playbook

An Ansible Playbook is a YAML file containing a series of plays. Each play defines a set of tasks to run on a group of Ansible hosts. Playbooks can include variables, conditionals, and handlers to add flexibility.

Key Components of Playbooks:

  • Hosts: These define the target machines on which the tasks are executed. The hosts field specifies which group or machine to apply the playbook to.
  • Tasks: These are the actual steps executed by Ansible, such as installing software or restarting services.
  • Handlers: Special tasks triggered when a change occurs, such as restarting a service after a configuration update.
  • Variables: Dynamic values used in playbooks to make tasks reusable across multiple environments.

Writing Your First Ansible Playbook

Let’s start with a simple playbook to install NGINX on an Ubuntu server.

1. Create an inventory file inventory.yml:

[webservers]
server1 ansible_host=192.168.1.10 ansible_user=ubuntu ansible_ssh_private_key_file=~/.ssh/id_rsa

2. Now create a playbook nginx_install.yml:

---
- name: Install NGINX on web server
  hosts: webservers
  become: yes
  tasks:
    - name: Update apt cache
      apt:
        update_cache: yes

    - name: Install NGINX
      apt:
        name: nginx
        state: present

3. Run the playbook:

 # ansible-playbook -i inventory.yml nginx_install.yml

Using Loops and Conditional in Playbooks

You can use loops to repeat tasks, and conditionals to execute tasks only when certain conditions are met.

Here is a simple playbook that uses a loop to install multiple packages on target servers.

---
- name: Install multiple packages
  hosts: webservers
  tasks:
    - name: Install necessary packages
      apt:
        name: "{{ item }}"
        state: present
      loop:
        - nginx
        - curl
        - git

You can also use when conditional in playbooks to install packages based on operating systems.

The following playbook installs the Nginx package only if the target host runs a Debian-based operating system.

- name: Install NGINX only if the host is Ubuntu
  apt:
    name: nginx
    state: present
  when: ansible_facts['os_family'] == "Debian"

Using include for Dynamic Task Execution

include_tasks allows you to include a separate task file dynamically. It’s useful when you need flexibility, such as including tasks conditionally or within a loop.

Example: Include Task Files Conditionally

Here, we will conditionally include different task files based on the operating system.

Main Playbook (site.yml):

---
- name: Configure Web Servers
  hosts: webservers
  tasks:
    - name: Include tasks for Debian
      include_tasks: debian.yml
      when: ansible_facts['os_family'] == "Debian"

    - name: Include tasks for RedHat
      include_tasks: redhat.yml
      when: ansible_facts['os_family'] == "RedHat"

Debian Task File (debian.yml):

---
- name: Install NGINX on Debian
  apt:
    name: nginx
    state: present

RedHat Task File (redhat.yml):

---
- name: Install NGINX on RedHat
  yum:
    name: nginx
    state: present

Using import for Static Task Execution

import_tasks is processed when the playbook starts, so it is static and executed unconditionally. It’s better for simpler scenarios where tasks don’t need to be dynamically included.

Import Task Files for Standard Tasks

In this example, we split common tasks into separate files and use import_tasks to include them.

Main Playbook (site.yml):

---
- name: Common Server Configuration
  hosts: all
  tasks:
    - name: Import common tasks
      import_tasks: common_tasks.yml

Common Task File (common_tasks.yml):

---
- name: Update package cache
  apt:
    update_cache: yes

- name: Ensure NGINX is installed
  apt:
    name: nginx
    state: present

Reusing Tasks Across Playbooks

You can reuse the same set of tasks across multiple playbooks. This is useful for defining common actions, like setting up users or configuring security settings.

Reusing Tasks Across Playbooks

First Playbook (playbook1.yml):

---
- name: Web Server Setup
  hosts: webservers
  tasks:
    - import_tasks: setup_users.yml

Second Playbook (playbook2.yml):

---
- name: Database Server Setup
  hosts: dbservers
  tasks:
    - import_tasks: setup_users.yml

Task File (setup_users.yml):

---
- name: Create user John
  user:
    name: john
    state: present

- name: Add John to sudo group
  user:
    name: john
    groups: sudo
    append: yes

Now, both playbook1.yml and playbook2.yml can reuse the setup_users.yml task file.

Using Variables and Facts in Playbooks

Ansible allows you to define variables using YAML syntax. Variables allow you to define values that can be reused across tasks, which helps avoid hardcoding values in multiple places. This improves playbook maintainability.

Ansible facts automatically gather data about the target hosts, such as OS, IP address, or hardware details. You can use facts to make decisions dynamically during playbook execution,.

Let’s look at an optimized playbook that installs different web servers (nginx or httpd) based on the host’s operating system. For flexibility, we’ll combine variables and Ansible facts.

- name: Install web server based on OS
  hosts: all
  vars:
    web_package_debian: nginx
    web_package_redhat: httpd
  tasks:
    - name: Install web server on Debian-based systems
      apt:
        name: "{{ web_package_debian }}"
        state: present
      when: ansible_facts['os_family'] == "Debian"

    - name: Install web server on RedHat-based systems
      yum:
        name: "{{ web_package_redhat }}"
        state: present
      when: ansible_facts['os_family'] == "RedHat"

Ansible Playbook Options with Examples

When running a playbook, you can use several command-line options to control how the playbook executes. Understanding these options will help you customize playbook execution for different environments and scenarios.

Below are some frequently used options in the playbook.

1. Using –check Mode (Dry Run)

The –check option runs the playbook in “dry run” mode. It simulates the changes without applying them, allowing you to test the playbook’s logic and see what would change.

 # ansible-playbook playbook.yml --check

Use this mode when you want to validate the playbook without making changes to the target system.

2. Using –diff for Configuration File Changes

The –diff option shows the differences between files before and after the playbook is applied. This is useful when you’re managing configuration files.

 # ansible-playbook playbook.yml --diff

Use this when working with configuration files to see exactly what changes will be made.

3. Using –verbose for Detailed Output

Ansible provides verbosity levels through the -v, -vv, -vvv, or -vvvv flags. The more vs you use, the more detailed the output becomes. For example:

 # ansible-playbook playbook.yml -vvv

Use this when you want to see detailed logs of each step in the playbook’s execution.

4. Using the debug Module

The debug module helps to print the value of variables or other information during playbook execution. This is useful for troubleshooting and ensuring that variables are set correctly.

- name: Debug variable value
  hosts: all
  tasks:
    - name: Print OS family
      debug:
        var: ansible_facts['os_family']

    - name: Print a custom message
      debug:
        msg: "Running on {{ inventory_hostname }} which is part of {{ ansible_facts['os_family'] }}"

Use the debug Ansible module when you want to inspect variables or outputs during playbook execution.

5. Using –start-at-task to Resume from a Specific Task

If a playbook fails or you want to test specific tasks, you can use the –start-at-task option to begin playbook execution from a particular task without starting from the beginning.

 # ansible-playbook playbook.yml --start-at-task="Install NGINX"

Use this to save time when debugging a specific part of the playbook.

6. Using ansible-lint to Validate Playbooks

ansible-lint is a tool that checks your playbooks for best practices and potential issues. It helps ensure that your playbook follows coding standards and doesn’t have syntax or logic errors.

First, install ansible-lint:

 # pip install ansible-lint

Then, run the linter:

 # ansible-lint playbook.yml

Use ansible-lint regularly to validate your playbooks for common errors and best practices.

7. Using –limit to Limit Target Hosts

The -l option allows you to limit the playbook execution to specific hosts or groups.

Example:

 # ansible-playbook playbook.yml -l webservers

This command limits the playbook execution to hosts in the webservers group.

8. Using –inventory to Specify Inventory File

By default, Ansible uses the inventory file specified in the default configuration. You can override it using the -i option.

Example:

 # ansible-playbook my_playbook.yml -i /path/to/inventory.ini

This command specifies a custom inventory file for the playbook.

9. Using –ask-become-pass to Prompt for Sudo Password

If your playbook requires sudo privileges, you can use –ask-become-pass to prompt for the password.

Example:

 # ansible-playbook playbook.yml --ask-become-pass

This command prompts you to enter the sudo password for tasks that require elevated privileges.

Conclusion

Ansible Playbooks provide a flexible way to automate IT operations. From simple tasks to complex workflows, playbooks can streamline configuration management and deployments. Start with simple playbooks and gradually build more complex, modular ones using roles and variables.

FAQs

1. Can I use conditionals in an Ansible Playbook?

Yes, conditionals can be applied using the when clause to run tasks only when specific conditions are met.

2. What is the purpose of the hosts keyword in playbook?

The hosts keyword specifies the target group of hosts on which the playbook tasks will be executed.

3. How do you handle errors in Ansible Playbooks?

You can handle errors using the ignore_errors directive or by using block and rescue sections for more advanced error handling.

4. What is the use of the become directive in Ansible Playbooks?

The become directive is used to escalate privileges, allowing tasks to run as a different user, such as root, when necessary.

About Hitesh Jethva

I am Hitesh Jethva, Founder and Author at Code2DevOps.com. With over 15 years of experience in DevOps and open source technologies, I am passionate about empowering teams through automation, continuous integration, and scalable solutions.

View all posts by Hitesh Jethva