Post

DevOps - Ansible: Advanced Playbooks and Role Development

DevOps - Ansible: Advanced Playbooks and Role Development

Intro

Ansible’s power of automation, and its capabilities extend far beyond basic playbooks. Advanced playbook features and role development allow you to create modular, reusable, and highly efficient automation workflows. This guide explores advanced concepts such as conditionals, strategies, custom roles, and dynamic task execution.


Step 1: Advanced Playbook Features

1.1 Using Conditionals

Conditionals enable tasks to run only when specific conditions are met.

Example:

Check if a service is running before attempting to restart it:

1
2
3
4
5
6
7
8
9
10
- name: Check if Apache is running
  ansible.builtin.shell: systemctl is-active apache2
  register: apache_status
  ignore_errors: yes

- name: Restart Apache if it is running
  ansible.builtin.service:
    name: apache2
    state: restarted
  when: apache_status.rc == 0

1.2 Delegation and Local Actions

Run tasks on a different host or locally on the control node.

Example:

Generate an SSL certificate locally and copy it to the target server:

1
2
3
4
5
6
7
8
9
- name: Generate SSL certificate locally
  ansible.builtin.command:
    cmd: openssl req -new -x509 -days 365 -nodes -out /tmp/cert.pem -keyout /tmp/key.pem
  delegate_to: localhost

- name: Copy SSL certificate to the server
  ansible.builtin.copy:
    src: /tmp/cert.pem
    dest: /etc/ssl/certs/cert.pem

1.3 Free Strategy for Parallel Execution

Use the free strategy to allow tasks to run independently on different hosts.

Example:

1
2
3
4
5
6
7
8
9
10
11
12
13
- name: Deploy Web Application with Free Strategy
  hosts: web_servers
  strategy: free
  tasks:
    - name: Install dependencies
      ansible.builtin.yum:
        name: httpd
        state: latest

    - name: Start web server
      ansible.builtin.service:
        name: httpd
        state: started

Step 2: Developing Custom Roles

Roles provide a structured way to organize playbooks into reusable components.

2.1 Creating a Role

Generate a role skeleton using ansible-galaxy:

1
ansible-galaxy init webserver_role

This creates the following directory structure:

1
2
3
4
5
6
7
8
9
10
11
12
13
webserver_role/
├── tasks/
│   └── main.yml
├── handlers/
│   └── main.yml
├── templates/
├── files/
├── vars/
│   └── main.yml
├── defaults/
│   └── main.yml
├── meta/
│   └── main.yml

2.2 Writing Tasks for the Role

Define tasks in tasks/main.yml:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
- name: Install Apache web server
  ansible.builtin.yum:
    name: httpd
    state: present

- name: Start Apache service
  ansible.builtin.service:
    name: httpd
    state: started

- name: Deploy index.html from template
  ansible.builtin.template:
    src: index.html.j2
    dest: /var/www/html/index.html

- name: Notify restart of Apache service if configuration changes
  notify:
    - Restart Apache Service

2.3 Adding Handlers

Define handlers in handlers/main.yml:

1
2
3
4
- name: Restart Apache Service
  ansible.builtin.service:
    name: httpd
    state: restarted

2.4 Using the Role in a Playbook

Reference the role in your playbook:

1
2
3
- hosts: web_servers
  roles:
    - role: webserver_role

Step 3: Dynamic Task Execution

Dynamic task execution enables flexible workflows based on runtime variables or conditions.

3.1 Looping Over Dynamic Variables

Iterate over a list of users to create accounts dynamically.

1
2
3
4
5
6
7
8
- name: Create users dynamically from a list of variables
  ansible.builtin.user:
    name: ""
    state: present
    shell: ""
  loop:
    - { name: "alice", shell: "/bin/bash" }
    - { name: "bob", shell: "/bin/zsh" }

3.2 Include Tasks Dynamically

Include tasks based on conditions.

1
2
3
4
5
6
7
- name: Include tasks for Debian-based systems only
  include_tasks: debian_tasks.yml
  when: ansible_os_family == "Debian"

- name: Include tasks for RedHat-based systems only
  include_tasks: redhat_tasks.yml
  when: ansible_os_family == "RedHat"

Step 4: Best Practices for Roles and Playbooks

  1. Follow Separation of Concerns
    Keep roles single-purposed (e.g., separate roles for web servers, databases, etc.).

  2. Use Descriptive Names
    Use meaningful names for roles, tasks, and variables to improve readability.

  3. Document Roles
    Provide clear documentation for required variables and role functionality.

  4. Avoid Hardcoding Values
    Use default variables in defaults/main.yml and allow overrides via vars.

  5. Test Roles Thoroughly
    Use tools like Molecule to test your roles in isolated environments.


Step 5: Advanced Role Usage with Parameters

Pass parameters to roles for flexibility.

Example Playbook with Parameterized Role:

1
2
3
4
5
6
- hosts: all
  roles:
    - role: webserver_role
      vars:
        http_port: 8080
        server_name: example.com

In the role’s tasks/main.yml, use these variables dynamically:

1
2
3
4
- name: Configure Apache virtual host file from template
  ansible.builtin.template:
    src: vhost.conf.j2
    dest: /etc/httpd/conf.d/.conf

Conclusion

Advanced Ansible playbooks and role development unlock powerful automation capabilities that are reusable, modular, and maintainable. By using features like conditionals, dynamic task execution, free strategies, and custom roles, you can create robust workflows tailored to complex environments. Follow best practices to ensure your Ansible projects remain scalable and easy to manage.

This post is licensed under CC BY 4.0 by the author.