Ansible playbooks are YAML files that describe the desired state of a set of managed servers using a series of tasks. Advanced playbook authoring goes beyond simple task lists to include: roles (reusable, shareable task bundles with a standard directory structure), handlers (tasks triggered only when a change occurs, such as restarting Nginx only when its configuration changes), variables and templates (Jinja2-powered configuration file generation from variables), conditionals and loops (applying tasks selectively or iteratively), and Ansible Vault (encrypting sensitive variables like passwords and API keys). This guide covers these intermediate to advanced patterns for writing production-quality Ansible playbooks on RHEL 9 to automate common server administration tasks.

Prerequisites

  • Ansible installed on RHEL 9 with SSH access to managed nodes

Step 1 — Handlers (Conditional Service Restarts)

# handlers only trigger when a task reports 'changed'
# /srv/ansible/nginx-config.yml
---
- name: Configure Nginx
  hosts: webservers
  become: true
  tasks:
    - name: Deploy Nginx configuration
      template:
        src: nginx.conf.j2
        dest: /etc/nginx/nginx.conf
        validate: 'nginx -t -c %s'  # Validate before replacing
      notify: Restart Nginx           # Trigger the handler only if changed

    - name: Deploy site configuration
      copy:
        src: mysite.conf
        dest: /etc/nginx/conf.d/mysite.conf
      notify: Reload Nginx

  handlers:
    - name: Restart Nginx
      systemd:
        name: nginx
        state: restarted
    - name: Reload Nginx
      systemd:
        name: nginx
        state: reloaded

Step 2 — Variables, Templates, and Loops

# Jinja2 template: /srv/ansible/templates/vhost.conf.j2
server {
    listen {{ http_port | default(80) }};
    server_name {{ server_name }};
    root /var/www/{{ server_name }};
}

# Playbook using loops to create multiple vhosts
---
- hosts: webservers
  become: true
  vars:
    vhosts:
      - name: site1.example.com
        port: 80
      - name: site2.example.com
        port: 8080
  tasks:
    - name: Create document roots
      file:
        path: /var/www/{{ item.name }}
        state: directory
        owner: nginx
      loop: "{{ vhosts }}"

    - name: Deploy vhost configs
      template:
        src: vhost.conf.j2
        dest: /etc/nginx/conf.d/{{ item.name }}.conf
      loop: "{{ vhosts }}"
      vars:
        server_name: "{{ item.name }}"
        http_port: "{{ item.port }}"
      notify: Reload Nginx

Step 3 — Ansible Vault (Encrypted Secrets)

# Create an encrypted variables file
ansible-vault create /srv/ansible/group_vars/all/vault.yml
# Enter and confirm vault password
# Add variables in the editor:
# vault_db_password: 'SecurePass123!'
# vault_api_key: 'abc123'

# Reference vault variables in playbooks:
# db_password: "{{ vault_db_password }}"

# Run playbook with vault password
ansible-playbook -i inventory.ini playbook.yml --ask-vault-pass
# Or use a password file:
ansible-playbook -i inventory.ini playbook.yml --vault-password-file ~/.vault_pass

Step 4 — Roles (Reusable Task Bundles)

# Create a role structure
ansible-galaxy init roles/nginx

# roles/nginx/
#   tasks/main.yml     — tasks
#   handlers/main.yml  — handlers
#   templates/         — Jinja2 templates
#   defaults/main.yml  — default variable values
#   vars/main.yml      — role-specific variables

# Use the role in a playbook
---
- hosts: webservers
  become: true
  roles:
    - nginx
    - { role: certbot, domain: example.com }  # Role with variables

Conclusion

Advanced Ansible playbooks on RHEL 9 use handlers to minimise service disruptions (services only restart when configuration actually changes), Ansible Vault to keep credentials out of version control, and roles to build a library of reusable, tested automation code. The most important operational practice is storing all playbooks and inventory in a Git repository — this creates an audit trail of every infrastructure change and allows rolling back to a known-good configuration by reverting a commit and re-running the playbook.

Next steps: How to Install Ansible on RHEL 9, How to Install Terraform on RHEL 9, and How to Install Jenkins on RHEL 9.