# Happy automating! [RAPIDE][BOT]# Fichier: python_cheats/cheatsheets/ansible.txt
# Cheatsheet Ansible - Guide Complet


Voici l'ordre de rangement logique pour cette cheatsheet Ansible :

1. **INTRODUCTION ANSIBLE** [OK] (déjà en premier - correct)
2. **INSTALLATION** [OK] (déjà en 2e - correct)
3. **CONFIGURATION** [OK] (déjà en 3e - correct)
4. **INVENTORY (INVENTAIRE)** [OK] (déjà en 4e - correct)
5. **VARIABLES & PRECEDENCE** (monter avant les commandes ad-hoc)
6. **FACTS** (monter juste après les variables)
7. **COMMANDES AD-HOC** (descendre après facts)
8. **MODULES ESSENTIELS** [OK] (garder après commandes ad-hoc)
9. **CONDITIONALS & LOOPS** (monter avant playbooks)
10. **PLAYBOOKS** (descendre après conditionals)
11. **TEMPLATES JINJA2** [OK] (garder après playbooks)
12. **HANDLERS** (déplacer juste après templates)
13. **INCLUDES & IMPORTS** (nouveau - après handlers)
14. **ROLES** [OK] (garder après includes)
15. **ANSIBLE VAULT** [OK] (garder après roles)
16. **ANSIBLE GALAXY & COLLECTIONS** [OK] (garder après vault)
17. **ERREUR HANDLING** (nouveau - après collections)
18. **STRATEGIES & PERFORMANCE** (nouveau - après error handling)
19. **DEBUGGING** (nouveau - après strategies)
20. **TESTING** (nouveau - après debugging)
21. **ANSIBLE AWX/TOWER** (nouveau - après testing)
22. **EXEMPLES COMPLETS** (nouveau - avant bonnes pratiques)
23. **BONNES PRATIQUES** (nouveau - avant troubleshooting)
24. **ANTI-PATTERNS À ÉVITER** (nouveau - après bonnes pratiques)
25. **TROUBLESHOOTING COMMUN** (nouveau - après anti-patterns)
26. **COMMANDES UTILES** (nouveau - après troubleshooting)
27. **RESSOURCES** (nouveau - avant checklist)
28. **CHECKLIST PRODUCTION** (nouveau - avant conclusion)


[OK] INTRODUCTION ANSIBLE

# Ansible est un outil d'automatisation IT open-source
# - Configuration management (gestion configuration)
# - Application deployment (déploiement applications)
# - Task automation (automatisation tâches)
# - Orchestration (orchestration multi-machines)

# Caractéristiques:
# - Agentless (sans agent sur machines cibles)
# - YAML pour playbooks (lisible, déclaratif)
# - Idempotent (peut rejouer sans effets secondaires)
# - Push-based (control node pousse vers managed nodes)
# - SSH pour communication (ou WinRM pour Windows)

# Architecture:
# Control Node (machine avec Ansible) -> SSH -> Managed Nodes (serveurs cibles)

# Concepts clés:
# - Inventory: Liste des serveurs à gérer
# - Playbook: Fichier YAML décrivant tâches à exécuter
# - Task: Une action à effectuer
# - Module: Code Python réutilisable pour tâches
# - Role: Organisation structurée de playbooks
# - Handler: Tâche déclenchée par notification


[OK] INSTALLATION

# === Linux (Ubuntu/Debian) ===

# Via apt
sudo apt update
sudo apt install ansible

# Via PPA (version récente)
sudo apt install software-properties-common
sudo add-apt-repository --yes --update ppa:ansible/ansible
sudo apt install ansible

# === Linux (RHEL/CentOS/Fedora) ===

# Via dnf/yum
sudo dnf install ansible          # Fedora
sudo yum install ansible          # CentOS/RHEL

# Via EPEL (Enterprise Linux)
sudo yum install epel-release
sudo yum install ansible

# === macOS ===

# Via Homebrew
brew install ansible

# Via pip
pip install ansible

# === Via pip (toutes plateformes) ===

# Installation globale
pip install ansible

# Installation dans venv (recommandé)
python -m venv ansible-venv
source ansible-venv/bin/activate  # Linux/Mac
ansible-venv\Scripts\activate     # Windows
pip install ansible

# Version spécifique
pip install ansible==9.0.1
pip install ansible-core==2.16.0

# === Installation depuis source ===

git clone https://github.com/ansible/ansible.git
cd ansible
pip install -e .

# === Vérification installation ===

ansible --version
ansible-playbook --version
ansible-galaxy --version

# Output:
# ansible [core 2.16.0]
#   config file = /etc/ansible/ansible.cfg
#   configured module search path = ...
#   ansible python module location = ...
#   python version = 3.11.6


[OK] CONFIGURATION

# === Fichiers de configuration (ordre de priorité) ===

# 1. ANSIBLE_CONFIG (variable d'environnement)
export ANSIBLE_CONFIG=/path/to/ansible.cfg

# 2. ansible.cfg (dans répertoire courant)
./ansible.cfg

# 3. ~/.ansible.cfg (home directory)
~/.ansible.cfg

# 4. /etc/ansible/ansible.cfg (global)
/etc/ansible/ansible.cfg

# === ansible.cfg - Configuration de base ===

[defaults]
# Inventory
inventory = ./inventory
# ou
inventory = ./inventory.ini
# ou
inventory = ./inventory.yml

# Remote user par défaut
remote_user = ansible

# SSH options
host_key_checking = False       # Désactiver vérification clés SSH
timeout = 30                    # Timeout SSH

# Privilege escalation
become = True                   # Devenir root par défaut
become_method = sudo
become_user = root
become_ask_pass = False

# Logging
log_path = ./ansible.log

# Output
stdout_callback = yaml          # Format output (yaml, json, debug)
ansible_managed = Managed by Ansible on {date}

# Performance
forks = 10                      # Nombre de process parallèles
gathering = smart               # Fact gathering (smart, implicit, explicit)
fact_caching = jsonfile
fact_caching_connection = /tmp/ansible_facts
fact_caching_timeout = 3600

# Retry files
retry_files_enabled = False     # Désactiver .retry files

[privilege_escalation]
become = True
become_method = sudo
become_user = root
become_ask_pass = False

[ssh_connection]
ssh_args = -o ControlMaster=auto -o ControlPersist=60s
pipelining = True               # Améliore performance
control_path = /tmp/ansible-ssh-%%h-%%p-%%r

[colors]
highlight = white
verbose = blue
warn = bright purple
error = red
debug = dark gray

# === Configuration minimale ===

# ansible.cfg
[defaults]
inventory = inventory.ini
remote_user = ubuntu
host_key_checking = False
become = True

# === Variables d'environnement ===

# Ansible
export ANSIBLE_CONFIG=/path/to/ansible.cfg
export ANSIBLE_INVENTORY=/path/to/inventory
export ANSIBLE_REMOTE_USER=ansible
export ANSIBLE_BECOME=true
export ANSIBLE_BECOME_METHOD=sudo

# SSH
export ANSIBLE_HOST_KEY_CHECKING=False
export ANSIBLE_SSH_ARGS="-o ForwardAgent=yes"

# Logging
export ANSIBLE_LOG_PATH=/var/log/ansible.log

# Debug
export ANSIBLE_DEBUG=True
export ANSIBLE_VERBOSE=vvv


[OK] INVENTORY (INVENTAIRE)

# === Format INI ===

# inventory.ini

# Hôtes individuels
web1.example.com
web2.example.com
192.168.1.10

# Groupes
[webservers]
web1.example.com
web2.example.com

[databases]
db1.example.com
db2.example.com

# Variables par hôte
[webservers]
web1.example.com ansible_host=192.168.1.10 ansible_port=22
web2.example.com ansible_host=192.168.1.11 ansible_user=admin

# Variables par groupe
[webservers:vars]
ansible_user=ubuntu
ansible_become=true
http_port=80
max_clients=200

# Groupes de groupes
[production:children]
webservers
databases

[production:vars]
env=production
datacenter=aws-us-east-1

# Patterns de noms
[webservers]
web[01:10].example.com    # web01 à web10
web[a:f].example.com      # weba à webf

# Range avec padding
[databases]
db[001:100].example.com   # db001 à db100

# === Format YAML ===

# inventory.yml
all:
  hosts:
    mail.example.com:
  children:
    webservers:
      hosts:
        web1.example.com:
          ansible_host: 192.168.1.10
          ansible_port: 22
        web2.example.com:
          ansible_host: 192.168.1.11
      vars:
        ansible_user: ubuntu
        http_port: 80
    
    databases:
      hosts:
        db1.example.com:
        db2.example.com:
      vars:
        ansible_user: postgres
    
    production:
      children:
        webservers:
        databases:
      vars:
        env: production
        region: us-east-1

# === Inventory dynamique (Python script) ===

#!/usr/bin/env python3
# dynamic_inventory.py

import json
import sys

def get_inventory():
    """Retourner inventory dynamique"""
    inventory = {
        'webservers': {
            'hosts': ['web1.example.com', 'web2.example.com'],
            'vars': {
                'ansible_user': 'ubuntu',
                'http_port': 80
            }
        },
        'databases': {
            'hosts': ['db1.example.com'],
            'vars': {
                'ansible_user': 'postgres'
            }
        },
        '_meta': {
            'hostvars': {
                'web1.example.com': {
                    'ansible_host': '192.168.1.10'
                },
                'web2.example.com': {
                    'ansible_host': '192.168.1.11'
                }
            }
        }
    }
    return inventory

def get_host(hostname):
    """Retourner variables pour un hôte spécifique"""
    hostvars = get_inventory()['_meta']['hostvars']
    return hostvars.get(hostname, {})

if __name__ == '__main__':
    if len(sys.argv) == 2 and sys.argv[1] == '--list':
        print(json.dumps(get_inventory(), indent=2))
    elif len(sys.argv) == 3 and sys.argv[1] == '--host':
        print(json.dumps(get_host(sys.argv[2]), indent=2))
    else:
        print("Usage: dynamic_inventory.py --list | --host <hostname>")
        sys.exit(1)

# Rendre exécutable
chmod +x dynamic_inventory.py

# Tester
./dynamic_inventory.py --list
./dynamic_inventory.py --host web1.example.com

# Utiliser
ansible -i dynamic_inventory.py all -m ping

# === Inventory AWS EC2 ===

# Installer plugin
pip install boto3 botocore

# aws_ec2.yml
plugin: amazon.aws.aws_ec2
regions:
  - us-east-1
  - eu-west-1
filters:
  instance-state-name: running
  tag:Environment: production
keyed_groups:
  - key: tags.Name
    prefix: tag_name
  - key: tags.Environment
    prefix: env
  - key: placement.availability_zone
    prefix: az
hostnames:
  - tag:Name
  - private-ip-address
compose:
  ansible_host: private_ip_address

# Utiliser
ansible-inventory -i aws_ec2.yml --graph
ansible-inventory -i aws_ec2.yml --list

# === Variables inventory ===

# Variables de connexion
ansible_host              # IP ou hostname
ansible_port              # Port SSH (défaut: 22)
ansible_user              # Username SSH
ansible_password          # Password (éviter, utiliser clés SSH)
ansible_ssh_private_key_file  # Chemin clé privée
ansible_connection        # Type connexion (ssh, local, docker)

# Variables privilege escalation
ansible_become            # Devenir autre user (true/false)
ansible_become_method     # Méthode (sudo, su, runas)
ansible_become_user       # User cible (défaut: root)
ansible_become_password   # Password sudo

# Variables Python
ansible_python_interpreter  # Chemin Python sur remote

# Exemples
[webservers]
web1 ansible_host=192.168.1.10 ansible_user=ubuntu ansible_port=2222
web2 ansible_host=192.168.1.11 ansible_ssh_private_key_file=~/.ssh/id_rsa

[local]
localhost ansible_connection=local ansible_python_interpreter=/usr/bin/python3

[OK] VARIABLES & PRECEDENCE

# === Ordre de précédence (du plus faible au plus fort) ===

1. command line values (e.g., -u my_user)
2. role defaults (defaults/main.yml)
3. inventory file or script group vars
4. inventory group_vars/all
5. playbook group_vars/all
6. inventory group_vars/*
7. playbook group_vars/*
8. inventory file or script host vars
9. inventory host_vars/*
10. playbook host_vars/*
11. host facts / cached set_facts
12. play vars
13. play vars_prompt
14. play vars_files
15. role vars (vars/main.yml)
16. block vars
17. task vars
18. include_vars
19. set_facts / registered vars
20. role (and include_role) params
21. include params
22. extra vars (-e, --extra-vars)

# === Définir variables ===

# 1. Command line
ansible-playbook playbook.yml -e "env=production version=1.0.0"
ansible-playbook playbook.yml --extra-vars "env=production"
ansible-playbook playbook.yml -e @vars.yml
ansible-playbook playbook.yml -e @vars.json

# 2. Inventory
[webservers]
web1 http_port=8080
web2 http_port=8081

[webservers:vars]
ansible_user=ubuntu
env=production

# 3. group_vars/
# group_vars/all.yml
---
ntp_server: ntp.example.com
dns_servers:
  - 8.8.8.8
  - 8.8.4.4

# group_vars/webservers.yml
---
http_port: 80
max_connections: 1000

# 4. host_vars/
# host_vars/web1.example.com.yml
---
http_port: 8080
server_name: web1

# 5. Playbook vars
---
- name: Example
  hosts: all
  vars:
    app_name: myapp
    app_version: 1.0.0

# 6. vars_files
---
- name: Example
  hosts: all
  vars_files:
    - vars/common.yml
    - vars/{{ env }}.yml

# 7. Role defaults
# roles/myapp/defaults/main.yml
---
app_port: 8000
app_user: deploy

# 8. Role vars
# roles/myapp/vars/main.yml
---
app_config_path: /etc/myapp

# 9. Task vars
- name: Install package
  apt:
    name: nginx
  vars:
    package_state: present

# 10. set_fact
- name: Set variable
  set_fact:
    deployment_time: "{{ ansible_date_time.iso8601 }}"
    cacheable: yes

# 11. register
- name: Get current user
  command: whoami
  register: current_user

- name: Show user
  debug:
    msg: "User: {{ current_user.stdout }}"

# === Variables spéciales (magic variables) ===

# Ansible facts
{{ ansible_hostname }}
{{ ansible_fqdn }}
{{ ansible_os_family }}
{{ ansible_distribution }}
{{ ansible_distribution_version }}
{{ ansible_kernel }}
{{ ansible_architecture }}
{{ ansible_processor_cores }}
{{ ansible_memtotal_mb }}
{{ ansible_default_ipv4.address }}
{{ ansible_default_ipv4.gateway }}
{{ ansible_all_ipv4_addresses }}
{{ ansible_date_time.iso8601 }}

# Inventory variables
{{ inventory_hostname }}          # Hostname dans inventory
{{ inventory_hostname_short }}    # Hostname court
{{ inventory_dir }}              # Chemin inventory directory
{{ inventory_file }}             # Chemin inventory file

# Groups
{{ groups }}                     # Tous les groupes
{{ groups['webservers'] }}       # Hôtes dans groupe
{{ groups.keys() }}              # Noms groupes
{{ group_names }}                # Groupes de l'hôte actuel

# Host variables
{{ hostvars }}                   # Variables de tous les hôtes
{{ hostvars[inventory_hostname] }}
{{ hostvars['web1.example.com']['ansible_default_ipv4']['address'] }}

# Play variables
{{ ansible_play_hosts }}         # Hôtes dans play actuel
{{ ansible_play_batch }}         # Batch actuel
{{ play_hosts }}                 # Alias de ansible_play_hosts

# Environment
{{ ansible_env }}                # Variables d'environnement
{{ ansible_env.HOME }}
{{ ansible_env.PATH }}
{{ lookup('env', 'HOME') }}

# Playbook
{{ playbook_dir }}               # Directory du playbook
{{ role_path }}                  # Path du role actuel

# === Variable scoping ===

# Global scope
- name: Set global fact
  set_fact:
    global_var: "value"
    cacheable: yes

# Play scope
- name: Play level
  hosts: all
  vars:
    play_var: "value"

# Block scope
- name: Block
  block:
    - debug:
        msg: "{{ block_var }}"
  vars:
    block_var: "value"

# Task scope
- name: Task
  debug:
    msg: "{{ task_var }}"
  vars:
    task_var: "value"


[OK] FACTS

# === Gather facts ===

# Activer/désactiver dans playbook
---
- name: With facts
  hosts: all
  gather_facts: yes      # Défaut

- name: Without facts
  hosts: all
  gather_facts: no

# Contrôler via config
[defaults]
gathering = smart        # smart, implicit, explicit, none

# === Voir tous les facts ===

# Ad-hoc
ansible all -m setup

# Filtrer facts
ansible all -m setup -a "filter=ansible_distribution*"
ansible all -m setup -a "filter=ansible_eth*"
ansible all -m setup -a "filter=ansible_mem*"

# Dans playbook
- name: Show facts
  debug:
    var: ansible_facts

- name: Show specific fact
  debug:
    var: ansible_default_ipv4

# === Facts importants ===

# Système
ansible_hostname
ansible_fqdn
ansible_domain
ansible_nodename
ansible_os_family          # Debian, RedHat, Windows
ansible_distribution       # Ubuntu, CentOS, Debian
ansible_distribution_version
ansible_distribution_release
ansible_distribution_major_version
ansible_kernel
ansible_architecture       # x86_64, aarch64
ansible_machine
ansible_system             # Linux, Windows, Darwin

# Hardware
ansible_processor
ansible_processor_cores
ansible_processor_count
ansible_processor_threads_per_core
ansible_processor_vcpus
ansible_memtotal_mb
ansible_memfree_mb
ansible_swaptotal_mb
ansible_swapfree_mb

# Network
ansible_interfaces
ansible_default_ipv4.address
ansible_default_ipv4.gateway
ansible_default_ipv4.netmask
ansible_default_ipv4.network
ansible_default_ipv6.address
ansible_all_ipv4_addresses
ansible_all_ipv6_addresses
ansible_hostname
ansible_fqdn
ansible_dns.nameservers

# Disques
ansible_devices
ansible_mounts
ansible_lvm

# Date/Time
ansible_date_time.iso8601
ansible_date_time.date
ansible_date_time.time
ansible_date_time.epoch
ansible_date_time.year
ansible_date_time.month
ansible_date_time.day

# Python
ansible_python_version
ansible_python.executable

# User
ansible_user_id
ansible_user_dir
ansible_user_shell
ansible_real_user_id
ansible_effective_user_id

# === Custom facts ===

# Créer fact file sur remote
# /etc/ansible/facts.d/custom.fact
[general]
env=production
region=us-east-1
datacenter=aws

[application]
name=myapp
version=1.0.0

# Format JSON
# /etc/ansible/facts.d/app.fact
{
  "env": "production",
  "version": "1.0.0"
}

# Script exécutable
# /etc/ansible/facts.d/dynamic.fact
#!/bin/bash
echo '{"uptime": "'$(uptime)'", "kernel": "'$(uname -r)'"}'

chmod +x /etc/ansible/facts.d/dynamic.fact

# Accéder custom facts
{{ ansible_local.custom.general.env }}
{{ ansible_local.app.version }}
{{ ansible_local.dynamic.uptime }}

# Déployer custom facts
- name: Create facts directory
  file:
    path: /etc/ansible/facts.d
    state: directory

- name: Copy custom fact
  copy:
    src: custom.fact
    dest: /etc/ansible/facts.d/custom.fact

- name: Reload facts
  setup:

# === Set custom facts (runtime) ===

- name: Set custom fact
  set_fact:
    deployment_id: "{{ ansible_date_time.epoch }}"
    app_version: "1.0.0"

- name: Use custom fact
  debug:
    msg: "Deployment {{ deployment_id }}, version {{ app_version }}"

# Cache facts
- name: Set cacheable fact
  set_fact:
    persistent_var: "value"
    cacheable: yes

# === Fact caching ===

# ansible.cfg
[defaults]
gathering = smart
fact_caching = jsonfile
fact_caching_connection = /tmp/ansible_facts
fact_caching_timeout = 86400

# Avec Redis
fact_caching = redis
fact_caching_connection = localhost:6379:0
fact_caching_timeout = 86400

# Désactiver fact cache
gathering = explicit


[OK] COMMANDES AD-HOC

# Syntaxe générale:
# ansible <pattern> -m <module> -a "<arguments>" [options]

# === Patterns ===

# Tous les hôtes
ansible all -m ping

# Un hôte
ansible web1.example.com -m ping

# Un groupe
ansible webservers -m ping

# Plusieurs groupes
ansible webservers:databases -m ping

# Intersection (webservers ET production)
ansible 'webservers:&production' -m ping

# Exclusion (webservers SAUF staging)
ansible 'webservers:!staging' -m ping

# Regex
ansible '~web[0-9]+' -m ping

# Range
ansible 'web[0:5]' -m ping

# === Options communes ===

-i inventory.ini          # Spécifier inventory
-m module_name            # Module à utiliser
-a "args"                 # Arguments du module
-u username               # Remote user
-b, --become              # Devenir root (sudo)
--become-user=user        # Devenir user spécifique
-K, --ask-become-pass     # Demander sudo password
-k, --ask-pass            # Demander SSH password
--private-key=file        # Clé SSH privée
-f 10                     # Forks (parallélisme)
-v, -vv, -vvv, -vvvv     # Verbosité
--check                   # Dry-run
--diff                    # Montrer différences
-e "var=value"            # Extra variables
-l, --limit=subset        # Limiter à subset hôtes
-t TAGS                   # Tags à exécuter
--list-hosts              # Lister hôtes matchés

# === Exemples commandes ===

# Ping tous les serveurs
ansible all -m ping

# Ping avec inventory spécifique
ansible all -i inventory.ini -m ping

# Exécuter commande shell
ansible webservers -m shell -a "uptime"
ansible all -m command -a "df -h"
ansible databases -m shell -a "free -m"

# Copier fichier
ansible webservers -m copy -a "src=/tmp/test.txt dest=/tmp/test.txt"

# Installer package
ansible webservers -b -m apt -a "name=nginx state=present"
ansible webservers -b -m yum -a "name=httpd state=latest"

# Gérer service
ansible webservers -b -m service -a "name=nginx state=restarted"
ansible all -b -m systemd -a "name=sshd state=started enabled=yes"

# Créer user
ansible all -b -m user -a "name=deploy state=present shell=/bin/bash"

# Gérer fichiers
ansible all -b -m file -a "path=/tmp/test state=directory mode=0755"
ansible all -b -m file -a "path=/tmp/test.txt state=absent"

# Git clone
ansible webservers -m git -a "repo=https://github.com/user/repo.git dest=/opt/app"

# Gather facts
ansible all -m setup
ansible all -m setup -a "filter=ansible_distribution*"
ansible web1 -m setup -a "filter=ansible_eth*"

# Dry-run (check mode)
ansible webservers -m apt -a "name=nginx state=present" --check

# Avec diff
ansible all -m copy -a "src=test.txt dest=/tmp/test.txt" --diff

# Limiter à certains hôtes
ansible webservers -m ping --limit web1,web2
ansible all -m ping --limit '!databases'

# Debug variable
ansible localhost -m debug -a "var=ansible_version"


[OK] MODULES ESSENTIELS

# === System ===

# ping - Test connectivité
ansible all -m ping

# setup - Gather facts
ansible all -m setup

# command - Exécuter commande (sans shell)
ansible all -m command -a "ls -la /tmp"

# shell - Exécuter commande (avec shell)
ansible all -m shell -a "ps aux | grep nginx"

# script - Exécuter script local sur remote
ansible all -m script -a "/path/to/script.sh"

# raw - Exécuter commande brute (sans Python)
ansible all -m raw -a "uptime"

# === Files ===

# copy - Copier fichier
ansible all -m copy -a "src=/tmp/test.txt dest=/tmp/test.txt mode=0644 owner=root"

# file - Gérer fichiers/directories
ansible all -m file -a "path=/tmp/test state=directory mode=0755"
ansible all -m file -a "path=/tmp/test.txt state=touch"
ansible all -m file -a "path=/tmp/old.txt state=absent"
ansible all -m file -a "src=/tmp/source dest=/tmp/link state=link"

# template - Templating Jinja2
ansible all -m template -a "src=template.j2 dest=/etc/config.conf"

# fetch - Récupérer fichier depuis remote
ansible all -m fetch -a "src=/var/log/syslog dest=/tmp/logs/"

# synchronize - Rsync
ansible all -m synchronize -a "src=/local/path dest=/remote/path"

# lineinfile - Modifier ligne dans fichier
ansible all -m lineinfile -a "path=/etc/hosts line='192.168.1.10 web1'"

# blockinfile - Insérer/modifier bloc
ansible all -m blockinfile -a "path=/etc/config block='line1\nline2'"

# replace - Remplacer pattern
ansible all -m replace -a "path=/etc/config regexp='old' replace='new'"

# === Packages ===

# apt - Package Debian/Ubuntu
ansible all -b -m apt -a "name=nginx state=present update_cache=yes"
ansible all -b -m apt -a "name=nginx state=latest"
ansible all -b -m apt -a "name=nginx state=absent"

# yum - Package RHEL/CentOS
ansible all -b -m yum -a "name=httpd state=present"

# dnf - Package Fedora
ansible all -b -m dnf -a "name=nginx state=latest"

# package - Package générique (détecte package manager)
ansible all -b -m package -a "name=vim state=present"

# pip - Package Python
ansible all -m pip -a "name=django state=present"
ansible all -m pip -a "name=flask version=2.0.0"

# === Services ===

# service - Gérer service
ansible all -b -m service -a "name=nginx state=started"
ansible all -b -m service -a "name=nginx state=restarted"
ansible all -b -m service -a "name=nginx state=stopped"
ansible all -b -m service -a "name=nginx enabled=yes"

# systemd - Systemd service
ansible all -b -m systemd -a "name=nginx state=started enabled=yes daemon_reload=yes"

# === Users & Groups ===

# user - Gérer users
ansible all -b -m user -a "name=deploy state=present shell=/bin/bash"
ansible all -b -m user -a "name=deploy password={{ 'password' | password_hash('sha512') }}"
ansible all -b -m user -a "name=deploy groups=sudo,docker append=yes"
ansible all -b -m user -a "name=deploy state=absent remove=yes"

# group - Gérer groupes
ansible all -b -m group -a "name=developers state=present gid=1500"

# authorized_key - Gérer clés SSH
ansible all -m authorized_key -a "user=deploy key='{{ lookup('file', '~/.ssh/id_rsa.pub') }}' state=present"

# === Git ===

# git - Clone/pull repository
ansible all -m git -a "repo=https://github.com/user/repo.git dest=/opt/app version=main"
ansible all -m git -a "repo=git@github.com:user/repo.git dest=/opt/app key_file=/home/user/.ssh/id_rsa"

# === Database ===

# mysql_db - Database MySQL
ansible db -m mysql_db -a "name=mydb state=present"

# mysql_user - User MySQL
ansible db -m mysql_user -a "name=user password=pass priv='mydb.*:ALL' state=present"

# postgresql_db - Database PostgreSQL
ansible db -m postgresql_db -a "name=mydb state=present"

# postgresql_user - User PostgreSQL
ansible db -m postgresql_user -a "name=user password=pass db=mydb priv=ALL"

# === Web ===

# uri - HTTP requests
ansible localhost -m uri -a "url=https://api.example.com/health status_code=200"

# get_url - Download fichier
ansible all -m get_url -a "url=https://example.com/file.tar.gz dest=/tmp/file.tar.gz"

# === Archives ===

# archive - Créer archive
ansible all -m archive -a "path=/opt/app dest=/tmp/app.tar.gz format=gz"

# unarchive - Extraire archive
ansible all -m unarchive -a "src=/tmp/app.tar.gz dest=/opt/ remote_src=yes"
ansible all -m unarchive -a "src=/local/app.tar.gz dest=/opt/"

# === Docker ===

# docker_container - Gérer container
ansible all -m docker_container -a "name=nginx image=nginx:latest state=started ports=80:80"

# docker_image - Gérer image
ansible all -m docker_image -a "name=nginx:latest source=pull"

# === Cloud ===

# ec2 - AWS EC2 instances
# s3 - AWS S3
# azure_rm_virtualmachine - Azure VMs
# gcp_compute_instance - GCP instances

# === Monitoring ===

# wait_for - Attendre condition
ansible all -m wait_for -a "port=22 state=started timeout=300"
ansible all -m wait_for -a "path=/tmp/done state=present"

# assert - Vérifier condition
ansible all -m assert -a "that='ansible_distribution == \"Ubuntu\"'"

# debug - Debug info
ansible localhost -m debug -a "msg='Hello World'"
ansible localhost -m debug -a "var=ansible_version"


[OK] CONDITIONALS & LOOPS

# === When (conditions) ===

# Condition simple
- name: Install on Debian
  apt:
    name: nginx
  when: ansible_os_family == "Debian"

# Multiple conditions (AND)
- name: Production Ubuntu
  debug:
    msg: "Production Ubuntu server"
  when:
    - ansible_distribution == "Ubuntu"
    - env == "production"

# OR condition
- name: Debian or Ubuntu
  debug:
    msg: "Debian-based system"
  when: ansible_distribution == "Debian" or ansible_distribution == "Ubuntu"

# Avec variables
- name: Check variable
  debug:
    msg: "Variable is defined"
  when: my_var is defined

- name: Check undefined
  debug:
    msg: "Variable not defined"
  when: my_var is undefined

# Boolean
- name: If enabled
  debug:
    msg: "Feature enabled"
  when: feature_enabled | bool

# Tests
when: ansible_distribution in ["Ubuntu", "Debian"]
when: ansible_memtotal_mb >= 4096
when: ansible_processor_vcpus > 2
when: result is succeeded
when: result is failed
when: result is changed
when: path is file
when: path is directory
when: path is link
when: path is exists
when: variable is string
when: variable is number
when: list is iterable
when: var is match("regex")
when: var is search("pattern")

# Registered variables
- name: Check service
  command: systemctl is-active nginx
  register: nginx_status
  ignore_errors: yes

- name: Service is running
  debug:
    msg: "Nginx is running"
  when: nginx_status.rc == 0

# === Loops ===

# Loop simple
- name: Install packages
  apt:
    name: "{{ item }}"
    state: present
  loop:
    - nginx
    - git
    - vim

# Loop avec dictionnaire
- name: Create users
  user:
    name: "{{ item.name }}"
    groups: "{{ item.groups }}"
    state: present
  loop:
    - {name: 'alice', groups: 'sudo'}
    - {name: 'bob', groups: 'developers'}
    - {name: 'charlie', groups: 'operators'}

# Loop avec when
- name: Install production packages
  apt:
    name: "{{ item }}"
    state: present
  loop:
    - nginx
    - redis
    - postgresql
  when: env == "production"

# Loop avec register
- name: Check services
  systemd:
    name: "{{ item }}"
    state: started
  loop:
    - nginx
    - postgresql
  register: service_results

- name: Show results
  debug:
    msg: "{{ item.name }} - {{ item.state }}"
  loop: "{{ service_results.results }}"

# Loop from variable
- name: Install from list
  apt:
    name: "{{ item }}"
  loop: "{{ packages_list }}"

# Loop with index
- name: Show with index
  debug:
    msg: "{{ index }}: {{ item }}"
  loop: "{{ my_list }}"
  loop_control:
    index_var: index

# Loop with label (cleaner output)
- name: Create users
  user:
    name: "{{ item.name }}"
    groups: "{{ item.groups }}"
  loop:
    - {name: 'alice', groups: 'sudo,developers'}
    - {name: 'bob', groups: 'developers'}
  loop_control:
    label: "{{ item.name }}"

# Loop with pause
- name: Restart services
  service:
    name: "{{ item }}"
    state: restarted
  loop:
    - service1
    - service2
  loop_control:
    pause: 5

# Loop dictionary
- name: Set environment variables
  lineinfile:
    path: /etc/environment
    line: "{{ item.key }}={{ item.value }}"
  loop: "{{ env_vars | dict2items }}"
  vars:
    env_vars:
      PATH: /usr/local/bin
      LANG: en_US.UTF-8

# Loop with range
- name: Create directories
  file:
    path: "/data/dir{{ item }}"
    state: directory
  loop: "{{ range(1, 11) | list }}"

# Loop until (retry)
- name: Wait for service
  uri:
    url: http://localhost:8080/health
    status_code: 200
  register: result
  until: result.status == 200
  retries: 10
  delay: 5

# Loop with_items (legacy, use loop)
- name: Old style
  apt:
    name: "{{ item }}"
  with_items:
    - nginx
    - git

# Loop alternatives (legacy)
with_dict: "{{ my_dict }}"          # Use loop + dict2items
with_fileglob: "*.txt"              # Use loop + fileglob
with_nested: [list1, list2]         # Use loop + product filter
with_subelements: [users, groups]   # Use loop + subelements filter


[OK] PLAYBOOKS

# === Structure basique ===

# playbook.yml
---
- name: Configure web servers
  hosts: webservers
  become: yes
  
  tasks:
    - name: Install nginx
      apt:
        name: nginx
        state: present
        update_cache: yes
    
    - name: Start nginx service
      service:
        name: nginx
        state: started
        enabled: yes

# Exécuter
ansible-playbook playbook.yml

# === Playbook avec variables ===

---
- name: Deploy application
  hosts: webservers
  become: yes
  
  vars:
    app_name: myapp
    app_version: "1.0.0"
    app_port: 8000
  
  tasks:
    - name: Install dependencies
      apt:
        name:
          - python3
          - python3-pip
          - git
        state: present
    
    - name: Clone application
      git:
        repo: "https://github.com/user/{{ app_name }}.git"
        dest: "/opt/{{ app_name }}"
        version: "{{ app_version }}"
    
    - name: Install Python packages
      pip:
        requirements: "/opt/{{ app_name }}/requirements.txt"

# === Playbook avec handlers ===

---
- name: Configure nginx
  hosts: webservers
  become: yes
  
  tasks:
    - name: Copy nginx config
      copy:
        src: nginx.conf
        dest: /etc/nginx/nginx.conf
      notify:
        - Restart nginx
        - Check nginx status
    
    - name: Copy site config
      template:
        src: site.conf.j2
        dest: /etc/nginx/sites-available/default
      notify: Restart nginx
  
  handlers:
    - name: Restart nginx
      service:
        name: nginx
        state: restarted
    
    - name: Check nginx status
      command: nginx -t

# === Playbook avec conditions ===

---
- name: OS-specific tasks
  hosts: all
  become: yes
  
  tasks:
    - name: Install Apache on Debian
      apt:
        name: apache2
        state: present
      when: ansible_os_family == "Debian"
    
    - name: Install Apache on RedHat
      yum:
        name: httpd
        state: present
      when: ansible_os_family == "RedHat"
    
    - name: Task only in production
      debug:
        msg: "Production environment"
      when: env == "production"
    
    - name: Multiple conditions (AND)
      debug:
        msg: "Ubuntu production"
      when:
        - ansible_distribution == "Ubuntu"
        - env == "production"
    
    - name: Multiple conditions (OR)
      debug:
        msg: "Debian or Ubuntu"
      when: ansible_distribution == "Debian" or ansible_distribution == "Ubuntu"

# === Playbook avec loops ===

---
- name: Loop examples
  hosts: localhost
  
  tasks:
    - name: Install multiple packages
      apt:
        name: "{{ item }}"
        state: present
      loop:
        - nginx
        - git
        - vim
    
    - name: Create multiple users
      user:
        name: "{{ item.name }}"
        groups: "{{ item.groups }}"
        state: present
      loop:
        - { name: 'alice', groups: 'sudo,developers' }
        - { name: 'bob', groups: 'developers' }
        - { name: 'charlie', groups: 'operators' }
    
    - name: Loop with dictionary
      debug:
        msg: "{{ item.key }} = {{ item.value }}"
      loop: "{{ my_dict | dict2items }}"
      vars:
        my_dict:
          key1: value1
          key2: value2
    
    - name: Loop with range
      debug:
        msg: "Number {{ item }}"
      loop: "{{ range(1, 10) | list }}"
    
    - name: Loop with until (retry)
      shell: /usr/bin/check_service.sh
      register: result
      until: result.rc == 0
      retries: 5
      delay: 10

# === Playbook avec blocks ===

---
- name: Block example
  hosts: webservers
  become: yes
  
  tasks:
    - name: Web server setup
      block:
        - name: Install nginx
          apt:
            name: nginx
            state: present
        
        - name: Start nginx
          service:
            name: nginx
            state: started
      
      rescue:
        - name: Print error
          debug:
            msg: "Failed to setup nginx"
        
        - name: Install alternative
          apt:
            name: apache2
            state: present
      
      always:
        - name: Check web server
          command: systemctl status nginx
          ignore_errors: yes

# === Playbook avec tags ===

---
- name: Complete deployment
  hosts: webservers
  become: yes
  
  tasks:
    - name: Install packages
      apt:
        name: nginx
        state: present
      tags:
        - install
        - nginx
    
    - name: Configure nginx
      copy:
        src: nginx.conf
        dest: /etc/nginx/nginx.conf
      tags:
        - config
        - nginx
    
    - name: Start nginx
      service:
        name: nginx
        state: started
      tags:
        - service
        - nginx
    
    - name: Install monitoring
      apt:
        name: prometheus-node-exporter
        state: present
      tags:
        - install
        - monitoring

# Exécuter tags spécifiques
ansible-playbook playbook.yml --tags "install"
ansible-playbook playbook.yml --tags "nginx"
ansible-playbook playbook.yml --tags "install,config"
ansible-playbook playbook.yml --skip-tags "monitoring"

# === Playbook avec variables avancées ===

---
- name: Variables examples
  hosts: webservers
  
  vars:
    # Variables simples
    app_name: myapp
    app_port: 8000
    
    # Listes
    packages:
      - nginx
      - git
      - vim
    
    # Dictionnaires
    db_config:
      host: localhost
      port: 5432
      name: mydb
      user: dbuser
    
    # Variables calculées
    app_url: "http://{{ ansible_hostname }}:{{ app_port }}"
  
  vars_files:
    - vars/common.yml
    - vars/{{ env }}.yml
  
  vars_prompt:
    - name: username
      prompt: "Enter username"
      private: no
    
    - name: password
      prompt: "Enter password"
      private: yes
  
  tasks:
    - name: Use variables
      debug:
        msg: "App: {{ app_name }}, DB: {{ db_config.host }}:{{ db_config.port }}"
    
    - name: Set fact (runtime variable)
      set_fact:
        deployment_time: "{{ ansible_date_time.iso8601 }}"
    
    - name: Register command output
      command: whoami
      register: current_user
    
    - name: Use registered variable
      debug:
        msg: "Current user: {{ current_user.stdout }}"

# === Playbook multi-play ===

---
# Play 1: Setup databases
- name: Configure database servers
  hosts: databases
  become: yes
  
  tasks:
    - name: Install PostgreSQL
      apt:
        name: postgresql
        state: present

# Play 2: Setup web servers
- name: Configure web servers
  hosts: webservers
  become: yes
  
  tasks:
    - name: Install nginx
      apt:
        name: nginx
        state: present

# Play 3: Deploy application
- name: Deploy application
  hosts: webservers
  become: yes
  become_user: deploy
  
  tasks:
    - name: Clone repository
      git:
        repo: https://github.com/user/app.git
        dest: /opt/app


[OK] TEMPLATES JINJA2

# === Template basique ===

# template.conf.j2
server {
    listen {{ http_port }};
    server_name {{ server_name }};
    
    location / {
        proxy_pass http://localhost:{{ app_port }};
    }
}

# Playbook
- name: Deploy config
  template:
    src: template.conf.j2
    dest: /etc/nginx/sites-available/mysite

# === Variables dans templates ===

# config.j2
# System info
Hostname: {{ ansible_hostname }}
OS: {{ ansible_distribution }} {{ ansible_distribution_version }}
IP: {{ ansible_default_ipv4.address }}

# Custom variables
App: {{ app_name }}
Version: {{ app_version }}
Port: {{ app_port }}

# Date
Generated: {{ ansible_date_time.iso8601 }}

# === Conditions dans templates ===

# nginx.conf.j2
server {
    listen {{ http_port }};
    
    {% if enable_ssl %}
    listen 443 ssl;
    ssl_certificate {{ ssl_cert_path }};
    ssl_certificate_key {{ ssl_key_path }};
    {% endif %}
    
    server_name {{ server_name }};
    
    {% if env == 'production' %}
    access_log /var/log/nginx/{{ app_name }}.access.log;
    error_log /var/log/nginx/{{ app_name }}.error.log;
    {% else %}
    access_log /dev/stdout;
    error_log /dev/stderr;
    {% endif %}
}

# === Loops dans templates ===

# hosts.j2
# /etc/hosts

127.0.0.1 localhost

{% for host in groups['webservers'] %}
{{ hostvars[host]['ansible_default_ipv4']['address'] }} {{ host}}
{% endfor %}

# nginx_upstreams.j2
upstream backend {
    {% for server in backend_servers %}
    server {{ server.host }}:{{ server.port }} weight={{ server.weight }};
    {% endfor %}
}

# Liste packages
{% for package in packages %}
- {{ package }}
{% endfor %}

# === Filtres Jinja2 ===

# String filters
{{ app_name | upper }}                    # MYAPP
{{ app_name | lower }}                    # myapp
{{ app_name | capitalize }}               # Myapp
{{ app_name | title }}                    # My App

# Default value
{{ variable | default('default_value') }}

# Liste filters
{{ packages | length }}                   # Nombre éléments
{{ packages | first }}                    # Premier élément
{{ packages | last }}                     # Dernier élément
{{ packages | join(', ') }}              # Joindre avec séparateur
{{ numbers | min }}                       # Minimum
{{ numbers | max }}                       # Maximum
{{ numbers | sum }}                       # Somme
{{ list | unique }}                       # Valeurs uniques
{{ list | sort }}                         # Trier

# Dict filters
{{ my_dict | dict2items }}               # Convertir en liste
{{ my_dict.keys() | list }}              # Clés
{{ my_dict.values() | list }}            # Valeurs

# JSON/YAML
{{ data | to_json }}                     # Convertir en JSON
{{ data | to_yaml }}                     # Convertir en YAML
{{ data | to_nice_json }}                # JSON formaté
{{ data | to_nice_yaml }}                # YAML formaté

# Path filters
{{ path | basename }}                    # Nom fichier
{{ path | dirname }}                     # Chemin directory
{{ '/path/to/file' | realpath }}        # Chemin absolu

# Hash/Password
{{ 'password' | password_hash('sha512') }}
{{ 'string' | hash('sha1') }}
{{ 'string' | b64encode }}
{{ 'c3RyaW5n' | b64decode }}

# IP/Network
{{ '192.168.1.10' | ipaddr }}           # Valider IP
{{ '192.168.1.0/24' | ipaddr('net') }}  # Network address

# Math
{{ 10 | int }}                          # Convertir en int
{{ '10.5' | float }}                    # Convertir en float
{{ number | abs }}                      # Valeur absolue
{{ 10 | pow(2) }}                       # Puissance (100)

# Tests
{% if variable is defined %}            # Variable existe
{% if variable is undefined %}          # Variable n'existe pas
{% if list is iterable %}               # Est iterable
{% if value is number %}                # Est nombre
{% if value is string %}                # Est string
{% if path is file %}                   # Est fichier
{% if path is directory %}              # Est directory

# === Templates complexes ===

# docker-compose.yml.j2
version: '3'

services:
  {% for service in services %}
  {{ service.name }}:
    image: {{ service.image }}
    {% if service.ports is defined %}
    ports:
      {% for port in service.ports %}
      - "{{ port }}"
      {% endfor %}
    {% endif %}
    {% if service.environment is defined %}
    environment:
      {% for key, value in service.environment.items() %}
      {{ key }}: {{ value }}
      {% endfor %}
    {% endif %}
  {% endfor %}

# systemd service template
# myapp.service.j2
[Unit]
Description={{ app_name }} Service
After=network.target

[Service]
Type={{ service_type | default('simple') }}
User={{ app_user }}
Group={{ app_group }}
WorkingDirectory={{ app_dir }}
ExecStart={{ app_exec }}
{% if env_vars is defined %}
{% for key, value in env_vars.items() %}
Environment="{{ key }}={{ value }}"
{% endfor %}
{% endif %}
Restart=always
RestartSec=10

[Install]
WantedBy=multi-user.target


[OK] HANDLERS

# === Handlers de base ===

# playbook.yml
---
- name: Configure web server
  hosts: webservers
  become: yes
  
  tasks:
    - name: Copy nginx config
      copy:
        src: nginx.conf
        dest: /etc/nginx/nginx.conf
      notify: Restart nginx
    
    - name: Copy site config
      template:
        src: site.conf.j2
        dest: /etc/nginx/sites-available/default
      notify:
        - Restart nginx
        - Check nginx
  
  handlers:
    - name: Restart nginx
      service:
        name: nginx
        state: restarted
    
    - name: Check nginx
      command: nginx -t

# === Handlers avec listen ===

tasks:
  - name: Update app config
    template:
      src: app.conf.j2
      dest: /etc/app/config.conf
    notify: Restart app services

handlers:
  - name: Restart web
    service:
      name: nginx
      state: restarted
    listen: Restart app services
  
  - name: Restart app
    service:
      name: myapp
      state: restarted
    listen: Restart app services

# === Forcer exécution handlers ===

- name: Flush handlers
  meta: flush_handlers

# Exemple
tasks:
  - name: Update config
    copy:
      src: config.conf
      dest: /etc/app/config.conf
    notify: Restart app
  
  # Force restart avant de continuer
  - name: Flush handlers now
    meta: flush_handlers
  
  - name: Wait for app
    wait_for:
      port: 8080
      state: started

# === Handlers avec conditions ===

handlers:
  - name: Restart nginx
    service:
      name: nginx
      state: restarted
    when: ansible_distribution == "Ubuntu"

# === Handlers dans roles ===

# roles/myapp/handlers/main.yml
---
- name: Restart myapp
  systemd:
    name: myapp
    state: restarted
    daemon_reload: yes

- name: Reload myapp
  systemd:
    name: myapp
    state: reloaded

- name: Validate config
  command: myapp validate-config
  changed_when: false


[OK] INCLUDES & IMPORTS

# === Import vs Include ===

# Import: Statique (au parse time)
# - Traité avant exécution
# - Ne peut pas utiliser variables dynamiques
# - Tags appliqués à toutes les tâches

# Include: Dynamique (au runtime)
# - Traité pendant exécution
# - Peut utiliser variables dynamiques
# - Tags appliqués conditionnellement

# === Import playbook ===

# site.yml
---
- import_playbook: webservers.yml
- import_playbook: databases.yml
- import_playbook: monitoring.yml

# Avec conditions (evaluated at parse time)
- import_playbook: production.yml
  when: env == "production"

# === Import tasks ===

# main.yml
---
- name: Setup server
  hosts: all
  tasks:
    - import_tasks: common.yml
    - import_tasks: "{{ ansible_os_family }}.yml"

# common.yml
---
- name: Update package cache
  apt:
    update_cache: yes
  when: ansible_os_family == "Debian"

- name: Install common packages
  package:
    name:
      - vim
      - git
      - curl

# === Include tasks ===

# main.yml
---
- name: Dynamic includes
  hosts: all
  tasks:
    - include_tasks: tasks.yml
      vars:
        app_name: myapp
    
    - include_tasks: "{{ item }}.yml"
      loop:
        - setup
        - deploy
        - configure

# Avec when
- include_tasks: production.yml
  when: env == "production"

# === Import role ===

---
- name: Setup with roles
  hosts: all
  tasks:
    - import_role:
        name: common
    
    - import_role:
        name: webserver
      vars:
        nginx_port: 8080

# === Include role ===

---
- name: Dynamic role inclusion
  hosts: all
  tasks:
    - include_role:
        name: "{{ item }}"
      loop:
        - common
        - webserver
        - monitoring

# Avec when
- include_role:
    name: database
  when: install_database | default(false)

# === Tags avec imports/includes ===

# Avec import_tasks (tags appliqués à toutes tâches)
- import_tasks: webserver.yml
  tags: webserver

# Avec include_tasks (tags pour include seulement)
- include_tasks: database.yml
  tags: database


[OK] ROLES

# === Structure role ===

roles/
└── webserver/
    ├── tasks/
    │   └── main.yml          # Tâches principales
    ├── handlers/
    │   └── main.yml          # Handlers
    ├── templates/
    │   └── nginx.conf.j2     # Templates
    ├── files/
    │   └── index.html        # Fichiers statiques
    ├── vars/
    │   └── main.yml          # Variables du role
    ├── defaults/
    │   └── main.yml          # Variables par défaut
    ├── meta/
    │   └── main.yml          # Métadonnées & dépendances
    ├── tests/
    │   ├── inventory
    │   └── test.yml          # Tests
    └── README.md             # Documentation

# === Créer un role ===

# Avec ansible-galaxy
ansible-galaxy role init webserver
ansible-galaxy role init roles/webserver

# Structure créée:
# webserver/
#   README.md
#   defaults/main.yml
#   files/
#   handlers/main.yml
#   meta/main.yml
#   tasks/main.yml
#   templates/
#   tests/inventory
#   tests/test.yml
#   vars/main.yml

# === Role complet: webserver ===

# roles/webserver/defaults/main.yml
---
# Variables par défaut (peuvent être overridées)
nginx_port: 80
nginx_user: www-data
nginx_worker_processes: auto
nginx_worker_connections: 1024

# roles/webserver/vars/main.yml
---
# Variables du role (priorité haute)
nginx_config_path: /etc/nginx/nginx.conf
nginx_sites_available: /etc/nginx/sites-available
nginx_sites_enabled: /etc/nginx/sites-enabled

# roles/webserver/tasks/main.yml
---
- name: Install nginx
  apt:
    name: nginx
    state: present
    update_cache: yes
  when: ansible_os_family == "Debian"

- name: Install nginx (RedHat)
  yum:
    name: nginx
    state: present
  when: ansible_os_family == "RedHat"

- name: Create nginx directories
  file:
    path: "{{ item }}"
    state: directory
    mode: '0755'
  loop:
    - /var/www/html
    - /var/log/nginx

- name: Copy nginx main config
  template:
    src: nginx.conf.j2
    dest: "{{ nginx_config_path }}"
    owner: root
    group: root
    mode: '0644'
  notify: Restart nginx

- name: Copy site config
  template:
    src: site.conf.j2
    dest: "{{ nginx_sites_available }}/default"
  notify: Reload nginx

- name: Enable site
  file:
    src: "{{ nginx_sites_available }}/default"
    dest: "{{ nginx_sites_enabled }}/default"
    state: link
  notify: Reload nginx

- name: Copy index.html
  copy:
    src: index.html
    dest: /var/www/html/index.html
    mode: '0644'

- name: Ensure nginx is started
  service:
    name: nginx
    state: started
    enabled: yes

# roles/webserver/handlers/main.yml
---
- name: Restart nginx
  service:
    name: nginx
    state: restarted

- name: Reload nginx
  service:
    name: nginx
    state: reloaded

- name: Check nginx config
  command: nginx -t
  changed_when: false

# roles/webserver/templates/nginx.conf.j2
user {{ nginx_user }};
worker_processes {{ nginx_worker_processes }};
pid /run/nginx.pid;

events {
    worker_connections {{ nginx_worker_connections }};
}

http {
    sendfile on;
    tcp_nopush on;
    tcp_nodelay on;
    keepalive_timeout 65;
    types_hash_max_size 2048;

    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 {{ nginx_sites_enabled }}/*;
}

# roles/webserver/templates/site.conf.j2
server {
    listen {{ nginx_port }};
    server_name {{ ansible_hostname }};

    root /var/www/html;
    index index.html index.htm;

    location / {
        try_files $uri $uri/ =404;
    }
}

# roles/webserver/files/index.html
<!DOCTYPE html>
<html>
<head>
    <title>Welcome</title>
</head>
<body>
    <h1>Nginx configured by Ansible!</h1>
</body>
</html>

# roles/webserver/meta/main.yml
---
galaxy_info:
  author: Your Name
  description: Nginx web server role
  company: Your Company
  license: MIT
  min_ansible_version: 2.9
  platforms:
    - name: Ubuntu
      versions:
        - focal
        - jammy
    - name: Debian
      versions:
        - buster
        - bullseye

dependencies: []
  # - role: common
  #   vars:
  #     some_var: value

# === Utiliser le role ===

# playbook.yml
---
- name: Setup web servers
  hosts: webservers
  become: yes
  
  roles:
    - webserver

# Avec variables
- name: Setup web servers
  hosts: webservers
  become: yes
  
  roles:
    - role: webserver
      vars:
        nginx_port: 8080
        nginx_worker_processes: 4

# Multiple roles
- name: Full setup
  hosts: all
  become: yes
  
  roles:
    - common
    - security
    - webserver
    - monitoring

# === Role avec dépendances ===

# roles/app/meta/main.yml
---
dependencies:
  - role: common
  - role: webserver
    vars:
      nginx_port: 8080
  - role: database
    when: install_database | default(false)

# === Role depuis Ansible Galaxy ===

# Installer role
ansible-galaxy install geerlingguy.nginx
ansible-galaxy install -r requirements.yml

# requirements.yml
---
roles:
  - name: geerlingguy.nginx
    version: 3.1.4
  
  - name: geerlingguy.postgresql
    version: 3.4.5
  
  - src: https://github.com/user/ansible-role-custom
    name: custom
    version: main

collections:
  - name: community.general
    version: 8.0.0

# Lister roles installés
ansible-galaxy role list

# Supprimer role
ansible-galaxy role remove geerlingguy.nginx


[OK] ANSIBLE VAULT

# === Chiffrer fichiers ===

# Créer fichier chiffré
ansible-vault create secrets.yml

# Éditer fichier chiffré
ansible-vault edit secrets.yml

# Chiffrer fichier existant
ansible-vault encrypt vars.yml

# Déchiffrer fichier
ansible-vault decrypt vars.yml

# Voir contenu sans déchiffrer
ansible-vault view secrets.yml

# Rechiffrer avec nouveau password
ansible-vault rekey secrets.yml

# === Chiffrer variables individuelles ===

# Chiffrer string
ansible-vault encrypt_string 'secret_password' --name 'db_password'

# Output:
# db_password: !vault |
#           $ANSIBLE_VAULT;1.1;AES256
#           ...encrypted data...

# Dans playbook/vars:
# vars.yml
---
db_host: localhost
db_user: admin
db_password: !vault |
          $ANSIBLE_VAULT;1.1;AES256
          66386439653966653033613039616535...

# === Utiliser vault ===

# Avec prompt password
ansible-playbook playbook.yml --ask-vault-pass

# Avec password file
echo "my_vault_password" > .vault_pass
chmod 600 .vault_pass
ansible-playbook playbook.yml --vault-password-file .vault_pass

# Avec script password
# vault-pass.sh
#!/bin/bash
# Récupérer password depuis AWS Secrets Manager, etc.
echo "my_password"

chmod +x vault-pass.sh
ansible-playbook playbook.yml --vault-password-file ./vault-pass.sh

# Dans ansible.cfg
[defaults]
vault_password_file = .vault_pass

# Variable d'environnement
export ANSIBLE_VAULT_PASSWORD_FILE=.vault_pass

# === Multiple vault passwords ===

# Créer avec vault ID
ansible-vault create --vault-id prod@prompt secrets_prod.yml
ansible-vault create --vault-id dev@.vault_pass_dev secrets_dev.yml

# Utiliser
ansible-playbook playbook.yml --vault-id prod@prompt --vault-id dev@.vault_pass_dev

# === Exemple secrets.yml ===

# secrets.yml (chiffré)
---
# Database
db_password: "super_secret_password"
db_root_password: "root_password"

# API Keys
aws_access_key: "AKIAIOSFODNN7EXAMPLE"
aws_secret_key: "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"

# SSH Keys
deploy_ssh_key: |
  -----BEGIN RSA PRIVATE KEY-----
  MIIEpAIBAAKCAQEA...
  -----END RSA PRIVATE KEY-----

# Tokens
api_token: "ghp_1234567890abcdefghijklmnopqrstuvwxyz"

# === Utiliser secrets dans playbook ===

---
- name: Deploy with secrets
  hosts: webservers
  become: yes
  
  vars_files:
    - secrets.yml
  
  tasks:
    - name: Configure database
      template:
        src: db_config.j2
        dest: /etc/app/config.yml
      vars:
        database:
          host: "{{ db_host }}"
          user: "{{ db_user }}"
          password: "{{ db_password }}"
    
    - name: Set environment variable
      lineinfile:
        path: /etc/environment
        line: "API_TOKEN={{ api_token }}"


[OK] ANSIBLE GALAXY & COLLECTIONS

# === Collections ===

# Installer collection
ansible-galaxy collection install community.general
ansible-galaxy collection install ansible.posix
ansible-galaxy collection install community.docker

# Installer version spécifique
ansible-galaxy collection install community.general:8.0.0

# Installer depuis requirements
# requirements.yml
---
collections:
  - name: community.general
    version: ">=8.0.0"
  
  - name: ansible.posix
    version: 1.5.4
  
  - name: community.docker
    source: https://galaxy.ansible.com
  
  - name: my_namespace.my_collection
    source: https://private.galaxy.example.com

ansible-galaxy collection install -r requirements.yml

# Lister collections
ansible-galaxy collection list

# Upgrade collection
ansible-galaxy collection install community.general --upgrade

# === Utiliser collections ===

# Dans playbook - FQCN (Fully Qualified Collection Name)
---
- name: Use collection modules
  hosts: all
  
  tasks:
    - name: Docker container
      community.docker.docker_container:
        name: nginx
        image: nginx:latest
        state: started
    
    - name: Archive files
      community.general.archive:
        path: /opt/app
        dest: /tmp/app.tar.gz

# Import collection
---
- name: Use collection
  hosts: all
  collections:
    - community.general
    - community.docker
  
  tasks:
    - name: Use module from collection
      docker_container:              # Pas besoin du namespace
        name: nginx
        image: nginx:latest

# === Créer collection ===

# Initialiser
ansible-galaxy collection init my_namespace.my_collection

# Structure:
# my_namespace/
# └── my_collection/
#     ├── README.md
#     ├── galaxy.yml
#     ├── plugins/
#     │   ├── modules/
#     │   ├── inventory/
#     │   ├── lookup/
#     │   └── filter/
#     ├── roles/
#     ├── playbooks/
#     └── docs/

# galaxy.yml
---
namespace: my_namespace
name: my_collection
version: 1.0.0
readme: README.md
authors:
  - Your Name <email@example.com>
description: My custom collection
license:
  - MIT
tags:
  - tools
  - cloud
dependencies:
  community.general: ">=8.0.0"

# Build collection
ansible-galaxy collection build

# Publish
ansible-galaxy collection publish my_namespace-my_collection-1.0.0.tar.gz


[OK] ERREUR HANDLING

# === Ignore errors ===

- name: Task that might fail
  command: /bin/false
  ignore_errors: yes

- name: Continue after error
  shell: risky_command.sh
  ignore_errors: true

# === Failed when ===

- name: Check command result
  command: /usr/bin/check_status.sh
  register: result
  failed_when: "'ERROR' in result.stdout"

- name: Complex failure condition
  shell: some_command
  register: result
  failed_when:
    - result.rc != 0
    - '"acceptable error" not in result.stderr'

# === Changed when ===

- name: Check if reboot needed
  command: needs-restarting -r
  register: reboot_check
  changed_when: reboot_check.rc == 1
  failed_when: reboot_check.rc > 1

- name: Never report as changed
  command: echo "hello"
  changed_when: false

# === Block error handling ===

- name: Handle errors with block
  block:
    - name: Primary task
      command: /bin/might_fail
    
    - name: Another task
      copy:
        src: file.txt
        dest: /tmp/
  
  rescue:
    - name: Handle failure
      debug:
        msg: "Tasks failed, running recovery"
    
    - name: Notify admin
      mail:
        to: admin@example.com
        subject: "Deployment failed"
        body: "Task failed on {{ inventory_hostname }}"
  
  always:
    - name: Always run cleanup
      file:
        path: /tmp/tempfile
        state: absent

# === Assert ===

- name: Validate conditions
  assert:
    that:
      - ansible_distribution == "Ubuntu"
      - ansible_distribution_version is version('20.04', '>=')
      - ansible_memtotal_mb >= 2048
    fail_msg: "System requirements not met"
    success_msg: "System requirements validated"

# === Fail module ===

- name: Check variable
  fail:
    msg: "Variable 'required_var' is not defined"
  when: required_var is undefined

- name: Fail with condition
  fail:
    msg: "Not enough memory: {{ ansible_memtotal_mb }}MB"
  when: ansible_memtotal_mb < 4096

# === Any errors fatal ===

---
- name: Critical playbook
  hosts: all
  any_errors_fatal: true
  
  tasks:
    - name: Critical task
      command: important_command

# === Max fail percentage ===

---
- name: Controlled failure
  hosts: all
  max_fail_percentage: 30    # Continue si < 30% échouent
  
  tasks:
    - name: Deploy application
      command: deploy.sh


[OK] STRATEGIES & PERFORMANCE

# === Strategies ===

# Linear (défaut) - attend que tous hôtes finissent tâche
---
- name: Linear strategy
  hosts: all
  strategy: linear
  tasks:
    - name: Task 1
      command: sleep 10

# Free - chaque hôte va aussi vite que possible
---
- name: Free strategy
  hosts: all
  strategy: free
  tasks:
    - name: Fast task
      command: echo "done"

# Debug - mode debug interactif
---
- name: Debug strategy
  hosts: all
  strategy: debug
  tasks:
    - name: Task to debug
      command: some_command

# Host pinned - exécute tous tasks sur un hôte avant le suivant
---
- name: Host pinned
  hosts: all
  strategy: host_pinned

# === Serial (batches) ===

# Exécuter par groupes
---
- name: Rolling update
  hosts: webservers
  serial: 2              # 2 hôtes à la fois
  
  tasks:
    - name: Update application
      command: update.sh

# Pourcentage
---
- name: Gradual rollout
  hosts: all
  serial: 20%           # 20% des hôtes à la fois

# Liste de batches
---
- name: Staged deployment
  hosts: all
  serial:
    - 1                 # D'abord 1 hôte
    - 3                 # Puis 3 hôtes
    - 5                 # Puis 5 hôtes
    - "100%"            # Puis le reste

# === Forks (parallélisme) ===

# ansible.cfg
[defaults]
forks = 20              # Nombre de process parallèles

# Command line
ansible-playbook playbook.yml --forks=50

# === Throttle (limiter parallélisme) ===

- name: Resource-intensive task
  command: heavy_process
  throttle: 1           # Un seul hôte à la fois

# === Async & Poll ===

# Tâche asynchrone
- name: Long running task
  command: /usr/bin/long_process.sh
  async: 3600           # Timeout 1h
  poll: 0               # Ne pas attendre (fire and forget)
  register: long_task

# Vérifier statut plus tard
- name: Check task status
  async_status:
    jid: "{{ long_task.ansible_job_id }}"
  register: job_result
  until: job_result.finished
  retries: 30
  delay: 10

# Async avec poll
- name: Long task with polling
  command: /usr/bin/process.sh
  async: 1800           # Timeout 30min
  poll: 10              # Vérifier toutes les 10s

# === Pipelining ===

# ansible.cfg
[ssh_connection]
pipelining = True       # Améliore performance SSH

# === Fact caching ===

# ansible.cfg
[defaults]
gathering = smart       # Gather facts si pas cached
fact_caching = jsonfile
fact_caching_connection = /tmp/ansible_facts
fact_caching_timeout = 86400

# Désactiver gathering si pas nécessaire
---
- name: No facts needed
  hosts: all
  gather_facts: no

# === ControlPersist ===

# ansible.cfg
[ssh_connection]
ssh_args = -o ControlMaster=auto -o ControlPersist=60s
control_path = /tmp/ansible-ssh-%%h-%%p-%%r

# === Optimizations ===

# 1. Désactiver fact gathering si inutile
gather_facts: no

# 2. Utiliser package au lieu de apt/yum
- package:            # Détecte automatiquement
    name: nginx

# 3. Limiter output avec no_log
- name: Sensitive task
  command: secret_command
  no_log: true

# 4. Utiliser changed_when pour éviter changed inutiles
- command: some_command
  changed_when: false

# 5. Mitogen plugin (installation séparée)
# ansible.cfg
[defaults]
strategy_plugins = /path/to/mitogen/ansible_mitogen/plugins/strategy
strategy = mitogen_linear


[OK] DEBUGGING

# === Debug module ===

# Message simple
- debug:
    msg: "Hello World"

# Variable
- debug:
    var: my_variable

# Multiple variables
- debug:
    msg: "User: {{ ansible_user }}, Host: {{ inventory_hostname }}"

# Registered variable
- command: whoami
  register: result

- debug:
    var: result

- debug:
    msg: "Output: {{ result.stdout }}"

# === Verbosity ===

# Différents niveaux
ansible-playbook playbook.yml -v      # Verbose
ansible-playbook playbook.yml -vv     # More verbose
ansible-playbook playbook.yml -vvv    # Very verbose (debug)
ansible-playbook playbook.yml -vvvv   # Connection debug

# Debug conditionnel
- debug:
    msg: "Debug info"
    verbosity: 2        # Seulement si -vv ou plus

# === Check mode (dry-run) ===

# Simuler sans changer
ansible-playbook playbook.yml --check

# Avec diff
ansible-playbook playbook.yml --check --diff

# Forcer exécution en check mode
- name: Always run
  command: some_command
  check_mode: no

# Skip en check mode
- name: Skip in check
  command: risky_command
  check_mode: yes

# === Step mode ===

# Confirmer chaque tâche
ansible-playbook playbook.yml --step

# === Start at task ===

# Commencer à une tâche spécifique
ansible-playbook playbook.yml --start-at-task="Install nginx"

# === Limit ===

# Limiter à certains hôtes
ansible-playbook playbook.yml --limit web1
ansible-playbook playbook.yml --limit "webservers:&production"
ansible-playbook playbook.yml --limit @/path/to/retry/file

# === List tasks ===

# Lister toutes les tâches
ansible-playbook playbook.yml --list-tasks

# Lister hosts
ansible-playbook playbook.yml --list-hosts

# Lister tags
ansible-playbook playbook.yml --list-tags

# === Syntax check ===

# Vérifier syntaxe
ansible-playbook playbook.yml --syntax-check

# === Ansible console ===

# Mode interactif
ansible-console all
ansible-console webservers

# Dans console:
> ping
> setup filter=ansible_distribution
> command uptime

# === Debug callbacks ===

# ansible.cfg
[defaults]
stdout_callback = debug    # Format debug détaillé
# ou
stdout_callback = yaml     # Format YAML
# ou
stdout_callback = json     # Format JSON

# === Profile tasks ===

# ansible.cfg
[defaults]
callback_whitelist = profile_tasks, timer

# Affiche temps d'exécution de chaque tâche

# === Troubleshooting ===

# 1. Voir facts
ansible hostname -m setup

# 2. Tester connexion
ansible all -m ping

# 3. Exécuter commande
ansible all -m command -a "uptime"

# 4. Verbose mode
ansible-playbook playbook.yml -vvv

# 5. Check vars
ansible-playbook playbook.yml -e "debug=true" --check

# 6. Log output
export ANSIBLE_LOG_PATH=./ansible.log
ansible-playbook playbook.yml


[OK] TESTING

# === Molecule (Testing Framework) ===

# Installer
pip install molecule[docker]
pip install molecule-docker

# Initialiser role avec molecule
cd roles/myrole
molecule init scenario

# Structure:
# molecule/
# └── default/
#     ├── converge.yml      # Playbook à tester
#     ├── molecule.yml      # Config molecule
#     └── verify.yml        # Tests

# 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

# converge.yml
---
- name: Converge
  hosts: all
  roles:
    - role: myrole

# verify.yml
---
- name: Verify
  hosts: all
  tasks:
    - name: Check nginx is installed
      package:
        name: nginx
        state: present
      check_mode: yes
      register: result
      failed_when: result.changed

# Commandes molecule
molecule create          # Créer instance test
molecule converge        # Exécuter role
molecule verify          # Run tests
molecule test            # Full test (create+converge+verify+destroy)
molecule destroy         # Détruire instance
molecule login           # SSH dans instance

# === Ansible-lint ===

# Installer
pip install ansible-lint

# Linter playbook
ansible-lint playbook.yml

# Linter role
ansible-lint roles/myrole/

# Config .ansible-lint
---
skip_list:
  - '106'  # Skip role name check
  - '204'  # Skip lines too long

exclude_paths:
  - .cache/
  - .git/
  - molecule/

# === Testinfra (Python Testing) ===

# Installer
pip install testinfra

# Test file: test_default.py
def test_nginx_installed(host):
    nginx = host.package("nginx")
    assert nginx.is_installed

def test_nginx_running(host):
    nginx = host.service("nginx")
    assert nginx.is_running
    assert nginx.is_enabled

def test_nginx_listening(host):
    assert host.socket("tcp://0.0.0.0:80").is_listening

def test_config_file(host):
    config = host.file("/etc/nginx/nginx.conf")
    assert config.exists
    assert config.user == "root"
    assert config.mode == 0o644

def test_website_accessible(host):
    cmd = host.run("curl -f http://localhost")
    assert cmd.rc == 0

# Exécuter tests
testinfra test_default.py --connection=ansible --ansible-inventory=inventory.ini


[OK] ANSIBLE AWX/TOWER

# AWX/Tower = Interface web pour Ansible

# === Installation AWX (Docker) ===

# Prérequis
# - Docker
# - Docker Compose
# - Minimum 4GB RAM

# Clone AWX
git clone https://github.com/ansible/awx.git
cd awx
git checkout <version>

# Install avec docker-compose
cd installer
ansible-playbook -i inventory install.yml

# Accès: http://localhost (admin/password)

# === Concepts AWX/Tower ===

# Organization: Groupement logique de teams, projets, inventories
# Team: Groupe d'utilisateurs avec permissions
# Project: Playbooks depuis SCM (Git)
# Inventory: Liste de hosts
# Credential: Authentification (SSH, Cloud, etc.)
# Job Template: Configuration pour exécuter playbook
# Workflow: Chaîne de job templates

# === Configuration via CLI (awx-cli) ===

# Installer
pip install awxkit

# Configurer
export TOWER_HOST=https://awx.example.com
export TOWER_USERNAME=admin
export TOWER_PASSWORD=password
export TOWER_VERIFY_SSL=false

# Créer organization
awx organizations create --name "My Org"

# Créer projet
awx projects create \
  --name "My Project" \
  --organization "My Org" \
  --scm-type git \
  --scm-url "https://github.com/user/ansible-playbooks.git"

# Créer inventory
awx inventory create \
  --name "Production" \
  --organization "My Org"

# Ajouter host
awx hosts create \
  --name "web1.example.com" \
  --inventory "Production" \
  --variables '{"ansible_host": "192.168.1.10"}'

# Créer job template
awx job_templates create \
  --name "Deploy App" \
  --project "My Project" \
  --playbook "deploy.yml" \
  --inventory "Production"

# Lancer job
awx job_templates launch "Deploy App"


[OK] EXEMPLES COMPLETS

# === Exemple 1: Setup LAMP Stack ===

---
- name: Install LAMP stack
  hosts: webservers
  become: yes
  
  vars:
    mysql_root_password: "SecurePassword123"
    app_db_name: "myapp"
    app_db_user: "appuser"
    app_db_password: "AppPassword456"
  
  tasks:
    - name: Update apt cache
      apt:
        update_cache: yes
        cache_valid_time: 3600
    
    - name: Install LAMP packages
      apt:
        name:
          - apache2
          - mysql-server
          - php
          - php-mysql
          - python3-pymysql
        state: present
    
    - name: Start Apache
      service:
        name: apache2
        state: started
        enabled: yes
    
    - name: Start MySQL
      service:
        name: mysql
        state: started
        enabled: yes
    
    - name: Set MySQL root password
      mysql_user:
        name: root
        password: "{{ mysql_root_password }}"
        login_unix_socket: /var/run/mysqld/mysqld.sock
        state: present
    
    - name: Create application database
      mysql_db:
        name: "{{ app_db_name }}"
        state: present
        login_user: root
        login_password: "{{ mysql_root_password }}"
    
    - name: Create database user
      mysql_user:
        name: "{{ app_db_user }}"
        password: "{{ app_db_password }}"
        priv: "{{ app_db_name }}.*:ALL"
        state: present
        login_user: root
        login_password: "{{ mysql_root_password }}"
    
    - name: Copy PHP test file
      copy:
        content: |
          <?php
          phpinfo();
          ?>
        dest: /var/www/html/info.php
        mode: '0644'
    
    - name: Configure Apache virtual host
      template:
        src: vhost.conf.j2
        dest: /etc/apache2/sites-available/myapp.conf
      notify: Restart Apache
    
    - name: Enable site
      command: a2ensite myapp.conf
      notify: Restart Apache
  
  handlers:
    - name: Restart Apache
      service:
        name: apache2
        state: restarted

# === Exemple 2: Deploy Django App ===

---
- name: Deploy Django application
  hosts: appservers
  become: yes
  become_user: root
  
  vars:
    app_name: mydjango
    app_user: django
    app_dir: "/opt/{{ app_name }}"
    git_repo: "https://github.com/user/mydjango.git"
    git_version: main
    python_version: "3.11"
  
  tasks:
    - name: Install system dependencies
      apt:
        name:
          - python3
          - python3-pip
          - python3-venv
          - git
          - nginx
          - postgresql
          - postgresql-contrib
          - libpq-dev
        state: present
        update_cache: yes
    
    - name: Create application user
      user:
        name: "{{ app_user }}"
        shell: /bin/bash
        home: "{{ app_dir }}"
        create_home: yes
        system: yes
    
    - name: Clone application repository
      git:
        repo: "{{ git_repo }}"
        dest: "{{ app_dir }}/code"
        version: "{{ git_version }}"
        force: yes
      become_user: "{{ app_user }}"
      notify: Restart gunicorn
    
    - name: Create virtual environment
      command: python3 -m venv {{ app_dir }}/venv
      args:
        creates: "{{ app_dir }}/venv/bin/activate"
      become_user: "{{ app_user }}"
    
    - name: Install Python dependencies
      pip:
        requirements: "{{ app_dir }}/code/requirements.txt"
        virtualenv: "{{ app_dir }}/venv"
      become_user: "{{ app_user }}"
      notify: Restart gunicorn
    
    - name: Copy environment file
      template:
        src: env.j2
        dest: "{{ app_dir }}/.env"
        owner: "{{ app_user }}"
        group: "{{ app_user }}"
        mode: '0600'
      notify: Restart gunicorn
    
    - name: Run migrations
      django_manage:
        command: migrate
        app_path: "{{ app_dir }}/code"
        virtualenv: "{{ app_dir }}/venv"
      become_user: "{{ app_user }}"
    
    - name: Collect static files
      django_manage:
        command: collectstatic
        app_path: "{{ app_dir }}/code"
        virtualenv: "{{ app_dir }}/venv"
      become_user: "{{ app_user }}"
    
    - name: Copy gunicorn systemd service
      template:
        src: gunicorn.service.j2
        dest: /etc/systemd/system/gunicorn.service
      notify:
        - Reload systemd
        - Restart gunicorn
    
    - name: Start gunicorn service
      systemd:
        name: gunicorn
        state: started
        enabled: yes
    
    - name: Copy nginx config
      template:
        src: nginx.conf.j2
        dest: /etc/nginx/sites-available/{{ app_name }}
      notify: Restart nginx
    
    - name: Enable nginx site
      file:
        src: /etc/nginx/sites-available/{{ app_name }}
        dest: /etc/nginx/sites-enabled/{{ app_name }}
        state: link
      notify: Restart nginx
    
    - name: Remove default nginx site
      file:
        path: /etc/nginx/sites-enabled/default
        state: absent
      notify: Restart nginx
  
  handlers:
    - name: Reload systemd
      systemd:
        daemon_reload: yes
    
    - name: Restart gunicorn
      systemd:
        name: gunicorn
        state: restarted
    
    - name: Restart nginx
      service:
        name: nginx
        state: restarted

# === Exemple 3: Docker Stack ===

---
- name: Deploy Docker application stack
  hosts: docker_hosts
  become: yes
  
  vars:
    docker_compose_version: "2.23.0"
    app_dir: /opt/myapp
    containers:
      - name: webapp
        image: myapp:latest
        ports:
          - "8080:8080"
      - name: redis
        image: redis:7-alpine
        ports:
          - "6379:6379"
  
  tasks:
    - name: Install Docker dependencies
      apt:
        name:
          - apt-transport-https
          - ca-certificates
          - curl
          - gnupg
          - lsb-release
        state: present
        update_cache: yes
    
    - name: Add Docker GPG key
      apt_key:
        url: https://download.docker.com/linux/ubuntu/gpg
        state: present
    
    - name: Add Docker repository
      apt_repository:
        repo: "deb [arch=amd64] https://download.docker.com/linux/ubuntu {{ ansible_distribution_release }} stable"
        state: present
    
    - name: Install Docker
      apt:
        name:
          - docker-ce
          - docker-ce-cli
          - containerd.io
        state: present
        update_cache: yes
    
    - name: Install Docker Compose
      get_url:
        url: "https://github.com/docker/compose/releases/download/v{{ docker_compose_version }}/docker-compose-linux-x86_64"
        dest: /usr/local/bin/docker-compose
        mode: '0755'
    
    - name: Start Docker service
      service:
        name: docker
        state: started
        enabled: yes
    
    - name: Create app directory
      file:
        path: "{{ app_dir }}"
        state: directory
        mode: '0755'
    
    - name: Copy docker-compose file
      template:
        src: docker-compose.yml.j2
        dest: "{{ app_dir }}/docker-compose.yml"
    
    - name: Pull Docker images
      community.docker.docker_image:
        name: "{{ item.image }}"
        source: pull
      loop: "{{ containers }}"
    
    - name: Start containers
      community.docker.docker_compose:
        project_src: "{{ app_dir }}"
        state: present
        pull: yes
    
    - name: Verify containers are running
      community.docker.docker_container_info:
        name: "{{ item.name }}"
      register: container_info
      loop: "{{ containers }}"
      failed_when: not container_info.container.State.Running

# === Exemple 4: Security Hardening ===

---
- name: Security hardening
  hosts: all
  become: yes
  
  vars:
    ssh_port: 22
    allowed_ssh_users:
      - admin
      - deploy
    fail2ban_maxretry: 3
    fail2ban_bantime: 3600
  
  tasks:
    - name: Update all packages
      apt:
        upgrade: dist
        update_cache: yes
        cache_valid_time: 3600
    
    - name: Install security packages
      apt:
        name:
          - ufw
          - fail2ban
          - unattended-upgrades
          - apt-listchanges
        state: present
    
    - name: Configure SSH
      lineinfile:
        path: /etc/ssh/sshd_config
        regexp: "{{ item.regexp }}"
        line: "{{ item.line }}"
      loop:
        - {regexp: '^#?PermitRootLogin', line: 'PermitRootLogin no'}
        - {regexp: '^#?PasswordAuthentication', line: 'PasswordAuthentication no'}
        - {regexp: '^#?X11Forwarding', line: 'X11Forwarding no'}
        - {regexp: '^#?MaxAuthTries', line: 'MaxAuthTries 3'}
      notify: Restart SSH
    
    - name: Allow SSH users
      lineinfile:
        path: /etc/ssh/sshd_config
        line: "AllowUsers {{ allowed_ssh_users | join(' ') }}"
        insertafter: EOF
      notify: Restart SSH
    
    - name: Configure UFW defaults
      ufw:
        direction: "{{ item.direction }}"
        policy: "{{ item.policy }}"
      loop:
        - {direction: 'incoming', policy: 'deny'}
        - {direction: 'outgoing', policy: 'allow'}
    
    - name: Allow SSH
      ufw:
        rule: allow
        port: "{{ ssh_port }}"
        proto: tcp
    
    - name: Enable UFW
      ufw:
        state: enabled
    
    - name: Configure fail2ban
      template:
        src: jail.local.j2
        dest: /etc/fail2ban/jail.local
      notify: Restart fail2ban
    
    - name: Start fail2ban
      service:
        name: fail2ban
        state: started
        enabled: yes
    
    - name: Configure automatic security updates
      copy:
        content: |
          APT::Periodic::Update-Package-Lists "1";
          APT::Periodic::Download-Upgradeable-Packages "1";
          APT::Periodic::AutocleanInterval "7";
          APT::Periodic::Unattended-Upgrade "1";
        dest: /etc/apt/apt.conf.d/20auto-upgrades
  
  handlers:
    - name: Restart SSH
      service:
        name: sshd
        state: restarted
    
    - name: Restart fail2ban
      service:
        name: fail2ban
        state: restarted



[OK] BONNES PRATIQUES

# === Structure de projet ===

# Projet bien organisé
ansible-project/
├── ansible.cfg                  # Configuration
├── inventory/
│   ├── production/
│   │   ├── hosts.ini
│   │   ├── group_vars/
│   │   │   ├── all.yml
│   │   │   ├── webservers.yml
│   │   │   └── databases.yml
│   │   └── host_vars/
│   │       ├── web1.yml
│   │       └── db1.yml
│   └── staging/
│       ├── hosts.ini
│       └── group_vars/
├── playbooks/
│   ├── site.yml
│   ├── webservers.yml
│   ├── databases.yml
│   └── deploy.yml
├── roles/
│   ├── common/
│   ├── webserver/
│   ├── database/
│   └── monitoring/
├── group_vars/
│   └── all.yml                  # Variables globales
├── host_vars/
├── files/                       # Fichiers statiques
├── templates/                   # Templates Jinja2
├── vars/
│   ├── production.yml
│   └── staging.yml
├── secrets/
│   └── vault.yml               # Fichiers chiffrés
├── filter_plugins/             # Filtres custom
├── library/                    # Modules custom
├── scripts/                    # Scripts utilitaires
├── tests/                      # Tests
├── requirements.yml            # Roles/collections
├── .gitignore
└── README.md

# === Nommage ===

# [OK] BON
- name: Install nginx package
- name: Start and enable nginx service
- name: Copy nginx configuration
- name: Restart nginx service

# [X] MAUVAIS
- name: Install
- name: Config
- shell: systemctl restart nginx

# === Variables ===

# [OK] Utiliser group_vars et host_vars
# group_vars/webservers.yml
http_port: 80
max_connections: 1000

# [X] Éviter variables dans playbook
vars:
  http_port: 80

# [OK] Nommage cohérent
app_name: myapp
app_port: 8000
app_user: deploy

# [X] Nommage incohérent
appName: myapp
port: 8000
deploy_user: deploy

# === Secrets ===

# [OK] Utiliser Ansible Vault
# secrets.yml (encrypted)
db_password: !vault |
    $ANSIBLE_VAULT;1.1;AES256
    ...

# [X] Passwords en clair
vars:
  db_password: "password123"

# === Idempotence ===

# [OK] Modules idempotents
- name: Ensure nginx is installed
  apt:
    name: nginx
    state: present

# [X] Shell non-idempotent
- name: Install nginx
  shell: apt-get install nginx

# [OK] Utiliser changed_when
- name: Check status
  command: systemctl is-active nginx
  register: result
  changed_when: false
  failed_when: result.rc not in [0, 3]

# === Modules vs Shell ===

# [OK] Préférer modules
- name: Create directory
  file:
    path: /opt/app
    state: directory
    mode: '0755'

# [X] Éviter shell
- name: Create directory
  shell: mkdir -p /opt/app && chmod 755 /opt/app

# [OK] Copier fichier
- copy:
    src: app.conf
    dest: /etc/app/app.conf

# [X] Shell
- shell: cp app.conf /etc/app/app.conf

# === Handlers ===

# [OK] Utiliser handlers pour redémarrages
tasks:
  - name: Update config
    copy:
      src: nginx.conf
      dest: /etc/nginx/nginx.conf
    notify: Restart nginx

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

# [X] Restart dans task
tasks:
  - copy:
      src: nginx.conf
      dest: /etc/nginx/nginx.conf
  - service:
      name: nginx
      state: restarted

# === Tags ===

# [OK] Utiliser tags pour flexibilité
- name: Install packages
  apt:
    name: nginx
  tags:
    - install
    - nginx

- name: Configure nginx
  template:
    src: nginx.conf.j2
    dest: /etc/nginx/nginx.conf
  tags:
    - config
    - nginx

# Exécuter
ansible-playbook site.yml --tags "nginx"
ansible-playbook site.yml --tags "install,config"

# === Roles ===

# [OK] Découper en roles réutilisables
roles/
├── common/       # Config commune à tous serveurs
├── webserver/    # Config serveurs web
└── database/     # Config bases de données

# [X] Tout dans un playbook monolithique

# === Templates ===

# [OK] Variables dans templates
# nginx.conf.j2
server {
    listen {{ nginx_port }};
    server_name {{ server_name }};
}

# [X] Valeurs en dur
server {
    listen 80;
    server_name example.com;
}

# === Error handling ===

# [OK] Gérer erreurs proprement
- name: Deploy application
  block:
    - name: Stop service
      service:
        name: myapp
        state: stopped
    
    - name: Update code
      git:
        repo: "{{ git_repo }}"
        dest: "{{ app_dir }}"
  
  rescue:
    - name: Rollback
      command: /usr/local/bin/rollback.sh
    
    - name: Notify team
      debug:
        msg: "Deployment failed, rolled back"
  
  always:
    - name: Start service
      service:
        name: myapp
        state: started

# === Documentation ===

# [OK] Documenter playbooks
---
# Playbook: Deploy web application
# Description: Deploys myapp to production web servers
# Requirements:
#   - Ubuntu 22.04+
#   - Ansible 2.14+
# Variables:
#   - app_version: Application version to deploy
#   - env: Environment (production/staging)
# Usage:
#   ansible-playbook deploy.yml -e "app_version=1.0.0"

- name: Deploy application
  hosts: webservers
  ...

# === Version Control ===

# .gitignore
*.retry
.vault_pass
group_vars/secrets.yml
host_vars/*/secrets.yml
.ansible_cache/
*.log

# [OK] Versionner
- Playbooks
- Roles
- Inventory (sauf credentials)
- Templates
- Variables

# [X] Ne pas versionner
- .vault_pass
- *.log
- .retry files
- Cache ansible

# === Testing ===

# [OK] Tester playbooks
# 1. Syntax check
ansible-playbook playbook.yml --syntax-check

# 2. Dry-run
ansible-playbook playbook.yml --check

# 3. Limit to test host
ansible-playbook playbook.yml --limit test-server

# 4. Use tags
ansible-playbook playbook.yml --tags "install" --check

# === Security ===

# [OK] Chiffrer secrets
ansible-vault encrypt secrets.yml

# [OK] SSH keys au lieu de passwords
ansible_ssh_private_key_file: ~/.ssh/id_rsa

# [OK] Limiter permissions sudo
# sudoers.d/ansible
ansible ALL=(ALL) NOPASSWD: /usr/bin/systemctl, /usr/bin/apt

# [OK] Valider inputs
- name: Validate environment
  assert:
    that:
      - env in ['production', 'staging', 'development']
      - app_version is defined
      - app_version is match("^[0-9]+\.[0-9]+\.[0-9]+$")

# === Performance ===

# [OK] Désactiver facts si inutile
gather_facts: no

# [OK] Utiliser fact caching
[defaults]
gathering = smart
fact_caching = jsonfile
fact_caching_connection = /tmp/ansible_facts

# [OK] Augmenter forks
[defaults]
forks = 20

# [OK] Pipelining SSH
[ssh_connection]
pipelining = True

# [OK] Strategy free pour tâches indépendantes
strategy: free

# === Maintenance ===

# [OK] Utiliser requirements.yml
---
roles:
  - name: geerlingguy.nginx
    version: 3.1.4

collections:
  - name: community.general
    version: ">=8.0.0"

# [OK] Pin versions
ansible>=2.14,<3.0
ansible-lint==6.22.0

# [OK] Mettre à jour régulièrement
ansible-galaxy install -r requirements.yml --force


[OK] ANTI-PATTERNS À ÉVITER

# [X] Shell/command au lieu de modules
- shell: mkdir -p /opt/app
# [OK] Utiliser file module
- file:
    path: /opt/app
    state: directory

# [X] Ignorer erreurs systématiquement
ignore_errors: yes
# [OK] Gérer proprement
rescue:
  - name: Handle error

# [X] Hardcoder values
listen 80;
# [OK] Variables
listen {{ nginx_port }};

# [X] Passwords en clair
db_password: "secret123"
# [OK] Vault
db_password: !vault |
    $ANSIBLE_VAULT;1.1;AES256...

# [X] Playbook monolithique
# [OK] Découper en roles

# [X] Variables dans playbook
vars:
  app_name: myapp
# [OK] group_vars ou host_vars

# [X] Pas de tags
# [OK] Tags pour flexibilité

# [X] Pas de error handling
# [OK] block/rescue/always

# [X] Gather facts toujours
# [OK] Désactiver si inutile

# [X] Shell pour tout
# [OK] Modules idempotents


[OK] TROUBLESHOOTING COMMUN

# === Problème: SSH Connection Timeout ===

# Cause: Host unreachable, firewall, wrong IP
# Solution:
# 1. Tester connexion SSH manuelle
ssh user@host

# 2. Vérifier inventory
ansible all -i inventory.ini --list-hosts

# 3. Tester ping
ansible all -m ping -vvv

# 4. Augmenter timeout
# ansible.cfg
[defaults]
timeout = 30

# === Problème: Permission Denied ===

# Cause: Mauvais user, pas de sudo
# Solution:
# 1. Vérifier remote_user
ansible all -m command -a "whoami"

# 2. Tester avec become
ansible all -m command -a "whoami" -b

# 3. Vérifier sudo sans password
# Sur remote host
sudo visudo
# Ajouter: ansible ALL=(ALL) NOPASSWD:ALL

# === Problème: Module not found ===

# Cause: Module/collection manquant
# Solution:
# 1. Installer collection
ansible-galaxy collection install community.general

# 2. Vérifier path modules
ansible-config dump | grep DEFAULT_MODULE_PATH

# 3. Utiliser FQCN
community.general.docker_container:

# === Problème: Variable undefined ===

# Cause: Variable non définie
# Solution:
# 1. Utiliser default
{{ my_var | default('default_value') }}

# 2. Vérifier variable définie
when: my_var is defined

# 3. Debug variables
- debug:
    var: my_var

- debug:
    msg: "{{ hostvars[inventory_hostname] }}"

# === Problème: Handler not triggered ===

# Cause: Task unchanged ou erreur avant handler
# Solution:
# 1. Vérifier changed status
- command: some_command
  changed_when: true  # Force changed
  notify: My handler

# 2. Forcer flush handlers
- meta: flush_handlers

# 3. Check handlers définis
ansible-playbook playbook.yml --list-tasks

# === Problème: Playbook slow ===

# Cause: Trop de hosts, gather facts, serial
# Solution:
# 1. Augmenter forks
ansible-playbook playbook.yml --forks=50

# 2. Désactiver gather facts
gather_facts: no

# 3. Utiliser strategy free
strategy: free

# 4. Profile tasks
# ansible.cfg
[defaults]
callback_whitelist = profile_tasks

# === Problème: Task fails intermittently ===

# Cause: Race condition, timing
# Solution:
# 1. Ajouter wait_for
- wait_for:
    port: 8080
    delay: 5
    timeout: 300

# 2. Retry avec until
- command: check_service.sh
  register: result
  until: result.rc == 0
  retries: 5
  delay: 10

# === Problème: Vault decrypt failed ===

# Cause: Mauvais password
# Solution:
# 1. Vérifier password file
cat .vault_pass

# 2. Re-encrypt avec nouveau password
ansible-vault rekey secrets.yml

# 3. Spécifier vault-id correct
ansible-playbook playbook.yml --vault-id prod@prompt

# === Problème: Cannot parse inventory ===

# Cause: Syntax error inventory
# Solution:
# 1. Valider syntax YAML
yamllint inventory.yml

# 2. Vérifier avec ansible-inventory
ansible-inventory -i inventory.yml --list

# 3. Debug inventory
ansible-inventory -i inventory.yml --graph


[OK] COMMANDES UTILES

# === Ansible Commands ===

# Ping tous les hôtes
ansible all -m ping

# Exécuter commande ad-hoc
ansible all -m command -a "uptime"
ansible all -m shell -a "ps aux | grep nginx"

# Gather facts
ansible all -m setup
ansible all -m setup -a "filter=ansible_distribution*"

# Copier fichier
ansible all -m copy -a "src=/tmp/file dest=/tmp/file"

# Gérer service
ansible all -b -m service -a "name=nginx state=restarted"

# Installer package
ansible all -b -m apt -a "name=nginx state=present"

# === Ansible-Playbook Commands ===

# Exécuter playbook
ansible-playbook playbook.yml

# Dry-run (check mode)
ansible-playbook playbook.yml --check

# Avec diff
ansible-playbook playbook.yml --check --diff

# Verbose
ansible-playbook playbook.yml -v
ansible-playbook playbook.yml -vvv

# Limiter à hosts
ansible-playbook playbook.yml --limit web1,web2
ansible-playbook playbook.yml --limit webservers

# Tags
ansible-playbook playbook.yml --tags "install,config"
ansible-playbook playbook.yml --skip-tags "monitoring"

# Variables
ansible-playbook playbook.yml -e "env=production version=1.0.0"
ansible-playbook playbook.yml -e @vars.yml

# Start at task
ansible-playbook playbook.yml --start-at-task="Deploy application"

# Step mode
ansible-playbook playbook.yml --step

# List
ansible-playbook playbook.yml --list-hosts
ansible-playbook playbook.yml --list-tasks
ansible-playbook playbook.yml --list-tags

# Syntax check
ansible-playbook playbook.yml --syntax-check

# === Ansible-Inventory Commands ===

# Lister inventory
ansible-inventory -i inventory.ini --list
ansible-inventory -i inventory.ini --graph

# Format JSON
ansible-inventory -i inventory.ini --list -y

# Voir variables host
ansible-inventory -i inventory.ini --host web1

# === Ansible-Galaxy Commands ===

# Roles
ansible-galaxy role init myrole
ansible-galaxy role install geerlingguy.nginx
ansible-galaxy role list
ansible-galaxy role remove geerlingguy.nginx
ansible-galaxy role install -r requirements.yml

# Collections
ansible-galaxy collection init my_namespace.my_collection
ansible-galaxy collection install community.general
ansible-galaxy collection list
ansible-galaxy collection install -r requirements.yml

# Search
ansible-galaxy role search nginx
ansible-galaxy collection search docker

# === Ansible-Vault Commands ===

# Créer fichier chiffré
ansible-vault create secrets.yml

# Éditer
ansible-vault edit secrets.yml

# Chiffrer fichier existant
ansible-vault encrypt vars.yml

# Déchiffrer
ansible-vault decrypt vars.yml

# Voir contenu
ansible-vault view secrets.yml

# Rekey (nouveau password)
ansible-vault rekey secrets.yml

# Chiffrer string
ansible-vault encrypt_string 'secret' --name 'var_name'

# === Ansible-Config Commands ===

# Voir config actuelle
ansible-config dump

# Voir config par défaut
ansible-config list

# Voir config effective
ansible-config view

# Valider config
ansible-config validate

# === Ansible-Doc Commands ===

# Documentation module
ansible-doc apt
ansible-doc service
ansible-doc copy

# Lister modules
ansible-doc -l
ansible-doc -l | grep docker

# Type de plugins
ansible-doc -t connection -l
ansible-doc -t callback -l

# === Ansible-Pull ===

# Pull mode (au lieu de push)
ansible-pull -U https://github.com/user/ansible.git

# Avec options
ansible-pull \
  -U https://github.com/user/ansible.git \
  -i hosts.ini \
  playbook.yml


[OK] RESSOURCES

# Documentation officielle:
# - https://docs.ansible.com/
# - https://docs.ansible.com/ansible/latest/user_guide/
# - https://docs.ansible.com/ansible/latest/modules/modules_by_category.html

# Galaxy (roles/collections):
# - https://galaxy.ansible.com/

# Best Practices:
# - https://docs.ansible.com/ansible/latest/user_guide/playbooks_best_practices.html

# AWX (Tower):
# - https://github.com/ansible/awx
# - https://docs.ansible.com/ansible-tower/

# Community:
# - GitHub: https://github.com/ansible/ansible
# - Forum: https://forum.ansible.com/
# - Reddit: r/ansible
# - IRC: #ansible on libera.chat

# Learning:
# - Ansible for DevOps (Jeff Geerling)
# - Mastering Ansible (James Freeman)
# - Red Hat Ansible Automation courses

# Collections populaires:
# - community.general
# - community.docker
# - ansible.posix
# - amazon.aws
# - community.kubernetes

# Roles populaires (Galaxy):
# - geerlingguy.nginx
# - geerlingguy.postgresql
# - geerlingguy.docker
# - geerlingguy.security

# Tools:
# - ansible-lint: Linter pour playbooks
# - molecule: Testing framework
# - ansible-navigator: TUI pour Ansible
# - semaphore: Web UI alternative à AWX


[OK] CHECKLIST PRODUCTION

# [OK] Setup
[WHITE_SQUARE] Ansible 2.14+ installé
[WHITE_SQUARE] SSH keys configurées
[WHITE_SQUARE] Inventory organisé (production/staging)
[WHITE_SQUARE] ansible.cfg configuré
[WHITE_SQUARE] Variables d'environnement définies

# [OK] Organisation
[WHITE_SQUARE] Structure de projet claire
[WHITE_SQUARE] Roles créés et documentés
[WHITE_SQUARE] Variables dans group_vars/host_vars
[WHITE_SQUARE] Templates Jinja2 organisés
[WHITE_SQUARE] README.md à jour

# [OK] Sécurité
[WHITE_SQUARE] Secrets chiffrés avec Vault
[WHITE_SQUARE] SSH keys au lieu de passwords
[WHITE_SQUARE] Sudo configuré correctement
[WHITE_SQUARE] Inventory production protégé
[WHITE_SQUARE] .gitignore configuré

# [OK] Code Quality
[WHITE_SQUARE] Playbooks avec noms descriptifs
[WHITE_SQUARE] Tasks idempotentes
[WHITE_SQUARE] Modules ansible au lieu de shell
[WHITE_SQUARE] Handlers pour redémarrages
[WHITE_SQUARE] Error handling avec block/rescue

# [OK] Testing
[WHITE_SQUARE] Syntax check passe
[WHITE_SQUARE] Dry-run testé
[WHITE_SQUARE] Tests sur staging avant prod
[WHITE_SQUARE] ansible-lint sans erreurs
[WHITE_SQUARE] Molecule tests (si applicable)

# [OK] Performance
[WHITE_SQUARE] Gather facts désactivé si inutile
[WHITE_SQUARE] Fact caching configuré
[WHITE_SQUARE] Forks optimisé
[WHITE_SQUARE] Pipelining activé
[WHITE_SQUARE] Strategy appropriée

# [OK] Documentation
[WHITE_SQUARE] README avec instructions
[WHITE_SQUARE] Variables documentées
[WHITE_SQUARE] Playbooks commentés
[WHITE_SQUARE] Requirements.yml à jour
[WHITE_SQUARE] Runbook pour incidents

# [OK] Monitoring
[WHITE_SQUARE] Logging configuré
[WHITE_SQUARE] Callbacks configurés
[WHITE_SQUARE] Profile tasks activé
[WHITE_SQUARE] Metrics collectées (si AWX/Tower)

# [OK] Déploiement
[WHITE_SQUARE] Stratégie de rollback définie
[WHITE_SQUARE] Serial/batch configuré
[WHITE_SQUARE] Health checks après déploiement
[WHITE_SQUARE] Notifications configurées
[WHITE_SQUARE] Post-deployment validation



[OK] CONCLUSION

# Ansible = Outil d'automatisation IT puissant et flexible

# Points clés:
# [OK] Agentless (SSH)
# [OK] YAML déclaratif
# [OK] Idempotent
# [OK] Large bibliothèque de modules
# [OK] Extensible (roles, collections)
# [OK] Community active

# Use cases:
# - Configuration management
# - Application deployment
# - Orchestration
# - Provisioning cloud
# - Security hardening
# - Continuous delivery

# Commencer:
# 1. Installer Ansible
# 2. Créer inventory simple
# 3. Tester avec ansible ad-hoc
# 4. Écrire premier playbook
# 5. Organiser en roles
# 6. Utiliser vault pour secrets
# 7. Automatiser avec CI/CD

# Pour aller plus loin:
# - AWX/Tower pour UI web
# - Molecule pour testing
# - Collections custom
# - Dynamic inventory
# - Ansible Navigator
# - Red Hat certification
