Accueil

Ansible Avancé : Roles, Collections, AWX et pipelines CI/CD

Devops
Difficulte: Advanced
15 min de lecture

Guide complet Ansible avancé : roles best practice, collections Galaxy, inventaires dynamiques AWS/GCP, AWX/Automation Controller, Molecule multi-distro et pipelines CI/CD GitHub Actions + GitLab CI.

Retour aux tutoriels

L'essentiel

  • La règle la plus mal comprise des rôles Ansible : defaults/main.yml a la priorité la plus basse et sert aux variables que l'utilisateur doit pouvoir changer (ports, chemins, versions) ; vars/main.yml a une priorité élevée et sert aux constantes internes (noms de paquets par OS, chemins système). Aucun secret ne va dans l'un ou l'autre — c'est le rôle d'Ansible Vault.
  • Une collection est le format de distribution moderne : elle empaquette roles, modules, plugins et playbooks dans un artefact versionné en semveransible.builtin en est une. Déclarez tout dans requirements.yml avec des bornes (amazon.aws: ">=7.0.0,<8.0.0") et installez dans le projet via ansible-galaxy collection install -r requirements.yml -p ./collections pour figer les versions par dépôt.
  • AWX, Ansible Tower et Automation Controller sont trois états du même produit : AWX est l'upstream open source sans support, Tower était le produit commercial 3.x, Automation Controller est son nom actuel dans Ansible Automation Platform 2.x. L'API REST est quasi identique, ce qui permet de configurer AWX as code avec la collection awx.awx ou awxkit (awx job_templates launch --monitor 42).
  • molecule test enchaîne le cycle complet create → prepare → converge → idempotency → verify → destroy : la phase idempotency rejoue le rôle et échoue s'il signale encore des changements, ce qui est le vrai test d'un rôle. Le multi-distribution passe par MOLECULE_DISTRO=rocky9 molecule test en local et une matrice rôle × distro en CI.
  • En CI/CD, le mot de passe Vault vit dans le coffre du runner, jamais dans le dépôt : echo "${{ secrets.ANSIBLE_VAULT_PASSWORD }}" > .vault_pass, puis --vault-password-file .vault_pass et rm -f .vault_pass en after_script. Ajoutez no_log: true sur toute tâche qui manipule un secret ; le vault-id étant inscrit dans l'en-tête $ANSIBLE_VAULT;1.2;AES256;prod, Ansible choisit seul la bonne clé.
Prérequis
Ce guide suppose une bonne connaissance d'Ansible (playbooks, inventaires, modules). Si vous débutez, commencez par le guide d'introduction Ansible. Pour ce tutoriel : Ansible Core 2.15+, Python 3.10+, Docker installé localement.

Pourquoi Ansible basique ne suffit pas à grande échelle

Ansible est séduisant dans sa forme la plus simple : un fichier hosts, un playbook YAML, et le tour est joué. Cette simplicité cache cependant des limitations structurelles qui deviennent douloureuses à mesure que l'infrastructure grandit.

Les symptômes sont toujours identiques. Des playbooks de 1500 lignes impossibles à maintenir. Des variables copiées-collées entre dix projets. Impossible de savoir quelle version d'un rôle est déployée en production. Aucun moyen de tester un changement avant de le pousser en prod. Des secrets en clair dans le dépôt git. Et zéro visibilité sur qui a lancé quoi et quand.

Ce guide couvre les solutions à chacun de ces problèmes :

  • Roles avancés : structure professionnelle, variables, dépendances, tags
  • Collections : packaging, versioning, Galaxy vs Automation Hub
  • Inventaires dynamiques : AWS EC2, GCP, groupes construits
  • AWX : interface web, RBAC, workflows graphiques, schedules
  • CI/CD : lint, test Molecule, déploiement automatisé
  • Sécurité : Vault avancé, secrets CI/CD, no_log

1. Roles avancés

Structure best practice d'un rôle

Un rôle bien structuré est un composant autonome, testable et réutilisable. La commande ansible-galaxy role init crée le squelette standard, que vous pouvez enrichir selon vos besoins.

# Créer un role avec la structure complète
ansible-galaxy role init mon_role

# Structure générée
mon_role/
├── README.md              # Documentation obligatoire
├── meta/
│   └── main.yml           # Métadonnées et dépendances
├── defaults/
│   └── main.yml           # Variables par défaut (basse priorité)
├── vars/
│   └── main.yml           # Variables internes (haute priorité)
├── tasks/
│   ├── main.yml           # Point d'entrée des tâches
│   ├── install.yml        # Tâches d'installation
│   ├── configure.yml      # Tâches de configuration
│   └── debian.yml         # Tâches spécifiques Debian
├── handlers/
│   └── main.yml           # Handlers (restart service, reload config)
├── templates/
│   └── nginx.conf.j2      # Templates Jinja2
├── files/
│   └── logrotate.conf     # Fichiers statiques
├── tests/
│   ├── inventory          # Inventaire de test minimal
│   └── test.yml           # Playbook de test basique
└── molecule/              # Tests Molecule (voir section 7)
    └── default/

defaults/main.yml vs vars/main.yml

La distinction entre ces deux répertoires est fondamentale et souvent mal comprise. Elle détermine qui peut surcharger quelle variable et à quel niveau.

# defaults/main.yml — Variables configurables par l'utilisateur
# Priorité la plus BASSE → facilement surchargées
nginx_port: 80
nginx_ssl_port: 443
nginx_worker_processes: auto
nginx_user: www-data
nginx_log_path: /var/log/nginx
nginx_sites_available: /etc/nginx/sites-available
nginx_sites_enabled: /etc/nginx/sites-enabled

# Ces valeurs peuvent être surchargées depuis :
# - group_vars/, host_vars/
# - -e "nginx_port=8080" en ligne de commande
# - Le playbook appelant
# vars/main.yml — Constantes internes du role
# Priorité HAUTE → difficiles à surcharger (usage avancé uniquement)
_nginx_packages:
  Debian:
    - nginx
    - nginx-extras
  RedHat:
    - nginx
    - nginx-mod-http-geoip2

_nginx_service_name: nginx
_nginx_config_dir: /etc/nginx
_nginx_pid_file: /run/nginx.pid

# Règle : jamais de secrets ici, uniquement des constantes structurelles
# tasks/main.yml — Point d'entrée avec include conditionnel
---
- name: Inclure les variables selon l'OS
  ansible.builtin.include_vars: "{{ ansible_os_family }}.yml"
  tags: always

- name: Installation Nginx
  ansible.builtin.include_tasks: install.yml
  tags: [nginx, install]

- name: Configuration Nginx
  ansible.builtin.include_tasks: configure.yml
  tags: [nginx, configure]

- name: Activation et démarrage du service
  ansible.builtin.service:
    name: "{{ _nginx_service_name }}"
    state: started
    enabled: true
  tags: [nginx, service]

meta/main.yml : dépendances et métadonnées

Le fichier meta/main.yml est crucial pour deux raisons : il documente le rôle pour Galaxy et définit ses dépendances qui seront résolues automatiquement.

# meta/main.yml
galaxy_info:
  author: votre_pseudo
  description: Role Nginx production-ready avec SSL et hardening
  company: Mon Entreprise
  license: MIT
  min_ansible_version: "2.15"
  platforms:
    - name: Ubuntu
      versions:
        - "22.04"
        - "24.04"
    - name: Debian
      versions:
        - "12"
    - name: EL
      versions:
        - "9"
  galaxy_tags:
    - nginx
    - web
    - proxy
    - ssl

# Dépendances : ces roles seront exécutés AVANT celui-ci
dependencies:
  - role: geerlingguy.certbot
    vars:
      certbot_email: [email protected]
    when: nginx_ssl_enabled | bool
  - role: common.firewall
    vars:
      firewall_allowed_tcp_ports:
        - "{{ nginx_port }}"
        - "{{ nginx_ssl_port }}"

handlers/main.yml

# handlers/main.yml
---
- name: Restart nginx
  ansible.builtin.service:
    name: "{{ _nginx_service_name }}"
    state: restarted
  listen: restart nginx

- name: Reload nginx
  ansible.builtin.service:
    name: "{{ _nginx_service_name }}"
    state: reloaded
  listen: reload nginx

- name: Test nginx config
  ansible.builtin.command: nginx -t
  changed_when: false
  listen: test nginx config

include_role dynamique et when conditionnel

La directive include_role permet d'inclure dynamiquement des roles à l'intérieur d'un play, avec des conditions et des variables calculées à l'exécution — impossible avec roles: statique.

# playbook avec include_role dynamique
---
- name: Configuration des serveurs web
  hosts: webservers
  become: true

  tasks:
    - name: Installer Nginx ou Apache selon le groupe
      ansible.builtin.include_role:
        name: "{{ 'nginx' if 'nginx_servers' in group_names else 'apache2' }}"
      vars:
        web_port: 80

    - name: Configurer SSL uniquement en production
      ansible.builtin.include_role:
        name: ssl_termination
      when: ansible_env.ENVIRONMENT | default('dev') == 'prod'

    - name: Appliquer les roles de monitoring pour chaque service
      ansible.builtin.include_role:
        name: "monitoring_{{ item }}"
      loop:
        - nginx
        - php_fpm
        - postgresql
      when: monitoring_enabled | default(true)

Contenu Premium

Ce tutoriel avancé est réservé aux membres premium.

9,90€ / mois
  • Tous les tutoriels avancés
  • Nouveaux contenus chaque semaine
  • Suivi de progression
  • Annulation à tout moment
MR

Écrit par

Morgann Riu

Expert en cybersécurité et administration Linux. Je partage mes connaissances à travers des tutoriels gratuits et des formations pour aider les administrateurs systèmes et développeurs à sécuriser leurs infrastructures.

Questions fréquentes

Quelle différence entre defaults/main.yml et vars/main.yml dans un rôle Ansible ?
defaults/main.yml contient les variables par défaut du rôle, avec la priorité la plus basse dans la hiérarchie Ansible. Elles peuvent être facilement surchargées depuis l'inventaire, le playbook ou la ligne de commande. vars/main.yml contient des variables internes au rôle avec une priorité élevée, difficiles à surcharger. La règle est simple : defaults/ pour les variables configurables par l'utilisateur (ports, chemins, versions), vars/ pour les constantes internes qui ne doivent pas changer (noms de packages selon l'OS, chemins système fixes). Ne mettez jamais de secrets dans aucun des deux : utilisez Ansible Vault.
Comment créer et publier sa propre collection Ansible Galaxy ?
Créez la structure avec ansible-galaxy collection init mon_namespace.ma_collection. Développez vos roles, modules, plugins dans la hiérarchie standard. Définissez la version semver dans galaxy.yml. Buildez avec ansible-galaxy collection build qui génère un tarball. Testez localement avec ansible-galaxy collection install mon_namespace-ma_collection-1.0.0.tar.gz. Publiez sur Galaxy avec ansible-galaxy collection publish --token le_tarball.tar.gz. Pour les environnements d'entreprise, utilisez Automation Hub (Red Hat) ou une instance Pulp/Galaxy NG privée avec l'option --server.
AWX vs Ansible Tower vs Automation Controller : quelles différences ?
Ce sont trois versions du même produit à des stades différents. AWX est la version open source upstream, développée en communauté, sans support, avec des releases fréquentes. Ansible Tower était le produit commercial Red Hat basé sur AWX, avec support payant (version 3.x). Automation Controller est le nom actuel du produit commercial dans Red Hat Ansible Automation Platform 2.x. En pratique : AWX pour les environnements auto-hébergés sans budget, Automation Controller pour les entreprises qui ont besoin de support Red Hat et d'intégrations EDA (Event-Driven Ansible). L'API REST est quasi-identique entre les trois.
Comment fonctionne Molecule pour tester des roles Ansible ?
Molecule orchestre le cycle de vie complet d'un rôle : création de l'environnement de test (instances Docker, VM Vagrant ou cloud), exécution du rôle (converge), vérification idempotence (idempotency), tests de validation (verify), puis destruction. Le scénario par défaut est dans molecule/default/. molecule.yml définit le driver (docker, vagrant, ec2) et les instances. converge.yml est le playbook qui applique le role. verify.yml contient les assertions (Testinfra en Python ou tasks Ansible). Le workflow complet est molecule test, qui enchaîne toutes les phases. Pour CI multi-distro, définissez plusieurs entrées dans platforms avec des images différentes.
Comment gérer les secrets Ansible Vault dans un pipeline CI/CD ?
La pratique recommandée est de stocker le mot de passe Vault dans le système de secrets du CI/CD, jamais dans le dépôt. Dans GitHub Actions, ajoutez ANSIBLE_VAULT_PASSWORD comme secret de dépôt. Dans le workflow, exportez-le dans un fichier temporaire : echo "${{ secrets.ANSIBLE_VAULT_PASSWORD }}" > .vault_pass et passez --vault-password-file .vault_pass à ansible-playbook. Supprimez le fichier après usage avec rm -f .vault_pass dans un bloc finally. Pour les vault-id multiples (prod/staging), utilisez plusieurs secrets CI et plusieurs --vault-id [email protected]_dev_pass --vault-id [email protected]_prod_pass. Ne loggez jamais les variables sensibles : no_log: true dans les tasks concernées.
Qu'est-ce que l'inventaire dynamique AWS EC2 et comment le configurer ?
Le plugin d'inventaire dynamique aws_ec2 interroge l'API AWS pour construire automatiquement l'inventaire de vos instances EC2, sans fichier hosts statique. Installez boto3 et le plugin amazon.aws. Créez aws_ec2.yml avec plugin: amazon.aws.aws_ec2, les régions et les filtres. Ansible interroge AWS à chaque exécution et construit les groupes depuis les tags EC2 (tag:Environment=prod devient le groupe tag_Environment_prod). La section keyed_groups permet de créer des groupes personnalisés depuis n'importe quel attribut. Avec constructed, vous créez des groupes conditionnels complexes. Les credentials AWS sont lus depuis les variables d'environnement (AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY) ou le profil ~/.aws/credentials.

Partager ce tutoriel

Cet article vous a plu ?

Cet article vous a-t-il été utile ?

Merci pour votre retour !

Commentaires

Recommandé pour vous

Article de fond sur le sujet

Checklist Sécurité Linux

30 points essentiels pour sécuriser un serveur Linux. Recevez aussi les nouveaux tutoriels par email.

Pas de spam. Désabonnement en 1 clic.