DEV Community

Cover image for Creating and Structuring Ansible Playbooks: A Complete Guide
Avesh
Avesh

Posted on

Creating and Structuring Ansible Playbooks: A Complete Guide

Ansible playbooks are YAML files that contain a series of tasks, configurations, and workflows to automate IT tasks. They are the foundation of Ansible automation, allowing users to describe the desired state of systems, deploy applications, and perform orchestrated actions across multiple servers. This guide will provide a detailed look at creating and structuring Ansible playbooks, with examples to illustrate each component.

What is an Ansible Playbook?

An Ansible playbook is a structured file written in YAML that tells Ansible what actions to take on target nodes (servers). Each playbook can contain one or more "plays," and each play defines tasks that apply to a specified set of hosts. A playbook may handle complex tasks like software installation, configuration management, or even orchestrate multi-tier application deployment.

Key Components of an Ansible Playbook

  1. Hosts: The group of machines or nodes on which the tasks are executed.
  2. Tasks: The individual actions Ansible performs on the managed nodes.
  3. Modules: Reusable Ansible code (like plugins) that performs specific tasks such as installing packages, creating files, or managing users.
  4. Handlers: Special tasks that trigger actions only when notified by other tasks, typically used for service restarts or reloading configurations.
  5. Variables: Dynamic values that can be assigned to various parts of the playbook, making it reusable across environments.
  6. Loops: Enable repetitive tasks in playbooks, reducing redundancy and improving manageability.
  7. Conditionals: Allow tasks to be executed only if certain conditions are met.

Basic Structure of an Ansible Playbook

Below is a simple example of an Ansible playbook:

---
- name: Basic Apache Setup
  hosts: webservers
  become: yes  # Allows privilege escalation (like sudo)

  tasks:
    - name: Install Apache
      apt:
        name: apache2
        state: present
      when: ansible_os_family == "Debian"

    - name: Start and enable Apache
      service:
        name: apache2
        state: started
        enabled: yes
Enter fullscreen mode Exit fullscreen mode

Explanation of Each Component

  • name: The description of the play, which provides context about what the playbook is doing.
  • hosts: Defines which group of hosts the play will target, like webservers (defined in the inventory file).
  • become: Enables privilege escalation, allowing tasks to run with elevated permissions (similar to sudo).
  • tasks: The list of actions to perform on the target hosts.
  • when: A conditional statement, in this case, ensuring the task only runs on Debian-based systems.

Detailed Example: Creating a Comprehensive Ansible Playbook

Here, we’ll create a more complete playbook that sets up a LAMP (Linux, Apache, MySQL, PHP) stack on a web server.

Step 1: Define the Playbook File Structure

To keep a playbook manageable, the typical structure may look like this:

lamp_setup.yml               # Main playbook file
vars/                        # Directory for variables
  └─ main.yml                # Variables file for customization
templates/                   # Directory for templates (e.g., configuration files)
  └─ apache.conf.j2          # Jinja2 template file for Apache configuration
Enter fullscreen mode Exit fullscreen mode

Step 2: Write the Playbook - lamp_setup.yml

---
- name: Install and Configure LAMP Stack
  hosts: webservers
  become: yes

  vars_files:
    - vars/main.yml

  tasks:
    - name: Update apt package manager
      apt:
        update_cache: yes

    - name: Install Apache, MySQL, PHP
      apt:
        name: "{{ item }}"
        state: present
      loop:
        - apache2
        - mysql-server
        - php
        - libapache2-mod-php
      notify:
        - Restart Apache

    - name: Start and enable Apache
      service:
        name: apache2
        state: started
        enabled: yes

    - name: Copy Apache configuration
      template:
        src: templates/apache.conf.j2
        dest: /etc/apache2/sites-available/000-default.conf
      notify:
        - Restart Apache

    - name: Set MySQL root password
      mysql_user:
        login_user: root
        login_password: ""
        name: root
        password: "{{ mysql_root_password }}"
        host_all: yes
        state: present

  handlers:
    - name: Restart Apache
      service:
        name: apache2
        state: restarted
Enter fullscreen mode Exit fullscreen mode

Step 3: Define Variables in vars/main.yml

---
mysql_root_password: "StrongPassword123"
Enter fullscreen mode Exit fullscreen mode

Step 4: Create an Apache Configuration Template - templates/apache.conf.j2

This is a Jinja2 template file for Apache configuration.

<VirtualHost *:80>
    ServerAdmin webmaster@localhost
    DocumentRoot /var/www/html

    <Directory /var/www/html>
        Options Indexes FollowSymLinks
        AllowOverride All
        Require all granted
    </Directory>

    ErrorLog ${APACHE_LOG_DIR}/error.log
    CustomLog ${APACHE_LOG_DIR}/access.log combined
</VirtualHost>
Enter fullscreen mode Exit fullscreen mode

Explanation of Each Section

  1. vars_files: This includes external variable files for better organization and customization, in this case, vars/main.yml.
  2. tasks:
    • Update apt package manager: Updates the package manager’s cache to ensure the latest packages are available.
    • Install Apache, MySQL, PHP: Installs the necessary packages in a loop for efficiency.
    • Start and enable Apache: Ensures that Apache is running and will start on boot.
    • Copy Apache configuration: Uses the template module to copy a Jinja2 templated configuration file into Apache’s configuration directory.
    • Set MySQL root password: Configures the MySQL root user with a specified password.
  3. handlers: The Restart Apache handler is defined to restart the Apache service whenever it’s notified by a task.

Using Loops and Conditionals

Ansible playbooks support loops and conditionals, which help make tasks more dynamic and efficient.

Example: Using Loops

- name: Install essential packages
  apt:
    name: "{{ item }}"
    state: present
  loop:
    - vim
    - curl
    - git
Enter fullscreen mode Exit fullscreen mode

Example: Using Conditionals

- name: Install Docker on Ubuntu
  apt:
    name: docker.io
    state: present
  when: ansible_distribution == "Ubuntu"
Enter fullscreen mode Exit fullscreen mode

Advanced Playbook: Role-Based Structure

For large projects, organizing tasks into roles improves readability and reusability. Each role typically contains its own tasks, handlers, templates, and variables.

Folder Structure

roles/
  └─ lamp/
      ├─ tasks/
      │   └─ main.yml
      ├─ handlers/
      │   └─ main.yml
      ├─ templates/
      │   └─ apache.conf.j2
      ├─ vars/
      │   └─ main.yml
Enter fullscreen mode Exit fullscreen mode

In the main playbook, you would call the role:

---
- name: Set up LAMP Stack using roles
  hosts: webservers
  roles:
    - lamp
Enter fullscreen mode Exit fullscreen mode

Running the Playbook

Once your playbook is ready, you can run it using the Ansible command:

ansible-playbook -i inventory lamp_setup.yml
Enter fullscreen mode Exit fullscreen mode

Best Practices for Ansible Playbooks

  1. Use YAML Syntax Properly: Ensure indentation is consistent (YAML is indentation-sensitive).
  2. Group Tasks Logically: Organize tasks by function to keep playbooks clean and readable.
  3. Use External Variable Files: Store frequently changed values, like passwords, in variable files to avoid hardcoding.
  4. Leverage Roles for Larger Projects: When playbooks grow, use roles to compartmentalize and manage tasks.
  5. Use Handlers for Efficiency: Handlers avoid redundant restarts by triggering only once, even if notified multiple times.

Conclusion

Ansible playbooks make automation simpler, scalable, and repeatable. By following best practices and structuring playbooks with tasks, variables, handlers, and roles, you can streamline complex workflows, improve infrastructure management, and simplify deployments. Whether you're deploying a single application or orchestrating an entire infrastructure, Ansible playbooks provide a flexible and powerful framework for automation.

Top comments (0)