1. Why roles? #
Reuse, test and share.
From a monolithic playbook to a role #
A growing playbook becomes unreadable. A role groups everything related to a component (installation, configuration, service) in a standardized structure: it can be reused across projects, tested independently and shared through Ansible Galaxy.
tasks/main.yml is mandatory.Creating the directory layout #
mkdir -p roles && cd roles
ansible-galaxy role init nginx
tree nginx
nginx/
├── README.md
├── defaults/main.yml # variables par défaut (priorité la plus basse)
├── files/ # fichiers copiés tels quels
├── handlers/main.yml # actions déclenchées par notify
├── meta/main.yml # métadonnées et dépendances
├── tasks/main.yml # point d'entrée du rôle
├── templates/ # fichiers Jinja2 (.j2)
├── tests/ # inventaire et playbook de test
└── vars/main.yml # variables internes (priorité élevée)2. Writing a complete role: nginx #
Tasks, defaults, templates and handlers.
defaults and tasks #
# roles/nginx/defaults/main.yml
nginx_port: 80
nginx_server_name: "{{ ansible_fqdn }}"
nginx_worker_processes: auto
nginx_enable_service: true# roles/nginx/tasks/main.yml
- name: Installer nginx
ansible.builtin.package:
name: nginx
state: present
- name: Déployer la configuration du site
ansible.builtin.template:
src: site.conf.j2
dest: /etc/nginx/conf.d/site.conf
owner: root
mode: "0644"
validate: nginx -t -c /etc/nginx/nginx.conf
notify: Recharger nginx
- name: Démarrer et activer nginx
ansible.builtin.service:
name: nginx
state: started
enabled: "{{ nginx_enable_service }}" Template and handler #
# roles/nginx/templates/site.conf.j2
server {
listen {{ nginx_port }};
server_name {{ nginx_server_name }};
root /var/www/{{ nginx_server_name }};
access_log /var/log/nginx/{{ nginx_server_name }}.access.log;
}# roles/nginx/handlers/main.yml
- name: Recharger nginx
ansible.builtin.service:
name: nginx
state: reloadedGood to know: A handler runs only once, at the end of the play, and only if a task that notifies it actually changed something. Force it to run immediately with - meta: flush_handlers.3. Using a role #
In a playbook, with its variables.
Calling the role #
# site.yml
- hosts: webservers
become: true
roles:
- role: nginx
vars:
nginx_port: 8080
- role: monitoring_agent
tags: [monitoring]ansible-playbook -i inventories/lab/hosts.ini site.yml
ansible-playbook site.yml --tags monitoring
ansible-playbook site.yml --list-tasksYou can also call it dynamically in the middle of a task list with include_role (conditional or looped execution) or import_role (static inclusion):
tasks:
- name: Installer le serveur web
ansible.builtin.include_role:
name: nginx
when: ansible_os_family == "Debian" Dependencies between roles #
# roles/wordpress/meta/main.yml
dependencies:
- role: nginx
vars: { nginx_port: 80 }
- role: mariadb4. Sharing with Ansible Galaxy and collections #
Don't reinvent the wheel.
Installing roles and collections #
ansible-galaxy role search nginx --author geerlingguy
ansible-galaxy role install geerlingguy.docker
ansible-galaxy collection install community.general ansible.posix
# Versionner les dépendances du projet
cat requirements.yml
roles:
- name: geerlingguy.docker
version: 7.4.1
collections:
- name: community.general
version: ">=9.0.0"
ansible-galaxy install -r requirements.ymlBest practices #
- One role = one responsibility; name it after the component (
nginx,postgres). - Prefix all variables with the role name (
nginx_port) to avoid collisions. - Put modifiable values in
defaults/and reservevars/for internal constants. - Make every task idempotent and test it with Molecule (see « Going further »).
Common mistake: Avoid writing a monster role that installs, configures and monitors ten components: it becomes impossible to test and reuse.
