Skip to main content

Ansible Roles


Chapter objectives

  • Understand the value of roles
  • Create the structure of a role
  • Use roles in playbooks
  • Share and reuse roles

1 - Why roles?

Problem without roles

Solution with roles

Advantages

Without rolesWith roles
Duplicated codeReusable code
Monolithic playbookIndependent modules
Hard to testUnit testable
No versioningVersionable

2 - Structure of a role

Standard tree

roles/
└── nginx/
├── README.md # Documentation
├── defaults/ # Variables par défaut (priorité basse)
│ └── main.yml
├── vars/ # Variables du rôle (priorité haute)
│ └── main.yml
├── tasks/ # Tâches principales
│ └── main.yml
├── handlers/ # Handlers
│ └── main.yml
├── templates/ # Templates Jinja2
│ └── nginx.conf.j2
├── files/ # Fichiers statiques
│ └── index.html
├── meta/ # Métadonnées et dépendances
│ └── main.yml
└── tests/ # Tests du rôle
├── inventory
└── test.yml

3 - Create a role

The ansible-galaxy command

# Créer un squelette de rôle
ansible-galaxy init roles/nginx

# Structure créée
roles/nginx/
├── README.md
├── defaults/
│ └── main.yml
├── files/
├── handlers/
│ └── main.yml
├── meta/
│ └── main.yml
├── tasks/
│ └── main.yml
├── templates/
├── tests/
│ ├── inventory
│ └── test.yml
└── vars/
└── main.yml

Complete example: nginx role

defaults/main.yml - Default variables

---
# defaults/main.yml
nginx_http_port: 80
nginx_https_port: 443
nginx_worker_processes: auto
nginx_worker_connections: 1024
nginx_server_names_hash_bucket_size: 64

nginx_vhosts: []
# Exemple:
# nginx_vhosts:
# - name: example.com
# root: /var/www/example.com
# port: 80

tasks/main.yml - Tasks

---
# tasks/main.yml
- name: Install nginx
apt:
name: nginx
state: present
update_cache: yes
tags: [nginx, packages]

- name: Configure nginx
template:
src: nginx.conf.j2
dest: /etc/nginx/nginx.conf
validate: nginx -t -c %s
notify: Reload nginx
tags: [nginx, configuration]

- name: Configure virtual hosts
template:
src: vhost.conf.j2
dest: "/etc/nginx/sites-available/{{ item.name }}"
loop: "{{ nginx_vhosts }}"
notify: Reload nginx
tags: [nginx, vhosts]

- name: Enable virtual hosts
file:
src: "/etc/nginx/sites-available/{{ item.name }}"
dest: "/etc/nginx/sites-enabled/{{ item.name }}"
state: link
loop: "{{ nginx_vhosts }}"
notify: Reload nginx
tags: [nginx, vhosts]

- name: Start and enable nginx
service:
name: nginx
state: started
enabled: yes
tags: [nginx, services]

handlers/main.yml - Handlers

---
# handlers/main.yml
- name: Reload nginx
service:
name: nginx
state: reloaded

- name: Restart nginx
service:
name: nginx
state: restarted

templates/nginx.conf.j2 - Template

# {{ ansible_managed }}
user www-data;
worker_processes {{ nginx_worker_processes }};
pid /run/nginx.pid;

events {
worker_connections {{ nginx_worker_connections }};
}

http {
sendfile on;
tcp_nopush on;
types_hash_max_size 2048;
server_names_hash_bucket_size {{ nginx_server_names_hash_bucket_size }};

include /etc/nginx/mime.types;
default_type application/octet-stream;

access_log /var/log/nginx/access.log;
error_log /var/log/nginx/error.log;

gzip on;

include /etc/nginx/conf.d/*.conf;
include /etc/nginx/sites-enabled/*;
}

meta/main.yml - Metadata

---
# meta/main.yml
galaxy_info:
author: DevOps Team
description: Install and configure Nginx
license: MIT
min_ansible_version: "2.10"
platforms:
- name: Ubuntu
versions:
- focal
- jammy
- name: Debian
versions:
- bullseye
- bookworm
galaxy_tags:
- nginx
- webserver
- web

dependencies: []
# dependencies:
# - role: common
# - role: firewall

4 - Use a role

In a playbook

---
# site.yml
- name: Configure web servers
hosts: webservers
become: yes

roles:
- nginx
- php-fpm
- app

With variables

---
- name: Configure web servers
hosts: webservers
become: yes

roles:
- role: nginx
nginx_http_port: 8080
nginx_vhosts:
- name: myapp.example.com
root: /var/www/myapp

Import vs Include

---
- name: Différentes façons d'inclure
hosts: webservers
become: yes

tasks:
# Statique - résolu au parsing
- import_role:
name: nginx
tags: [nginx]

# Dynamique - résolu à l'exécution
- include_role:
name: "{{ role_name }}"
vars:
role_name: nginx
when: install_nginx | bool

5 - Role dependencies

Define dependencies

# roles/app/meta/main.yml
---
dependencies:
- role: common
- role: nginx
nginx_vhosts:
- name: "{{ app_domain }}"
root: "{{ app_root }}"
- role: postgresql
when: app_db_type == 'postgresql'

Execution order


6 - Organizing tasks

Separate the tasks

# tasks/main.yml
---
- name: Include installation tasks
include_tasks: install.yml
tags: [install]

- name: Include configuration tasks
include_tasks: configure.yml
tags: [configure]

- name: Include OS-specific tasks
include_tasks: "{{ ansible_os_family }}.yml"
# tasks/install.yml
---
- name: Install nginx
apt:
name: nginx
state: present
# tasks/configure.yml
---
- name: Configure nginx
template:
src: nginx.conf.j2
dest: /etc/nginx/nginx.conf
# tasks/Debian.yml
---
- name: Debian-specific configuration
copy:
src: debian-default
dest: /etc/default/nginx

7 - Role testing

Test structure

roles/nginx/
└── molecule/
└── default/
├── converge.yml
├── molecule.yml
└── verify.yml

Simple test with Molecule

# molecule/default/molecule.yml
---
dependency:
name: galaxy
driver:
name: docker
platforms:
- name: instance
image: ubuntu:22.04
pre_build_image: true
provisioner:
name: ansible
verifier:
name: ansible
# molecule/default/converge.yml
---
- name: Converge
hosts: all
become: yes
roles:
- role: nginx
# molecule/default/verify.yml
---
- name: Verify
hosts: all
tasks:
- name: Check nginx is installed
command: nginx -v
changed_when: false

- name: Check nginx is running
service:
name: nginx
state: started
check_mode: yes
# Exécuter les tests
molecule test

8 - Best practices

# defaults/main.yml - Documenter les variables
---
# nginx_http_port: Port HTTP (default: 80)
nginx_http_port: 80

# nginx_worker_processes: Nombre de workers (default: auto)
nginx_worker_processes: auto

Naming

# ✅ Préfixer avec le nom du rôle
nginx_http_port: 80
nginx_worker_connections: 1024

# ❌ Noms génériques
http_port: 80
worker_connections: 1024

Idempotence

# ✅ Toujours idempotent
- name: Ensure nginx is installed
apt:
name: nginx
state: present

# ❌ Non idempotent
- name: Install nginx
command: apt-get install nginx

Documentation

<!-- README.md -->
# Role: nginx

Installs and configures Nginx web server.

## Requirements

- Ubuntu 20.04+ or Debian 11+

## Role Variables

| Variable | Default | Description |
|----------|---------|-------------|
| nginx_http_port | 80 | HTTP port |
| nginx_worker_processes | auto | Worker processes |

## Example Playbook

```yaml
- hosts: webservers
roles:
- role: nginx
nginx_http_port: 8080

---

## 9 - Complete project structure

```mermaid
graph TB
A[ansible-project/] --> B[ansible.cfg]
A --> C[inventory/]
A --> D[playbooks/]
A --> E[roles/]
A --> F[group_vars/]

E --> E1[common/]
E --> E2[nginx/]
E --> E3[postgresql/]
E --> E4[app/]

D --> D1[site.yml]
D --> D2[deploy.yml]

Main playbook

# playbooks/site.yml
---
- name: Apply common configuration
hosts: all
become: yes
roles:
- common

- name: Configure web servers
hosts: webservers
become: yes
roles:
- nginx
- php-fpm

- name: Configure database servers
hosts: databases
become: yes
roles:
- postgresql

- name: Deploy application
hosts: webservers
become: yes
roles:
- app

Summary

Key points
  • Roles enable code reuse
  • Use ansible-galaxy init to create a skeleton
  • Prefix variables with the role name
  • Test your roles with Molecule
  • Document in README.md

Practical exercises

  1. Create a "common" role for the basic configuration
  2. Create an "nginx" role with templates
  3. Add dependencies between roles
  4. Test your role with a playbook

← Variables and Facts | Templates →