# Cheatsheet Puppet - Guide Ultra-Détaillé pour Grands Débutants


[OK] CONCEPTS FONDAMENTAUX (EXPLICATIONS TRÈS DÉTAILLÉES)

# === QU'EST-CE QUE PUPPET ? ===

# Imagine que tu dois gérer 100 serveurs
# Sur chaque serveur, tu dois:
# 1. Installer Apache
# 2. Créer un utilisateur "webadmin"
# 3. Configurer le firewall
# 4. S'assurer que les services démarrent automatiquement

# Solution classique (compliquée):
# 1. Te connecter en SSH à chaque serveur (100 fois!)
# 2. Taper les mêmes commandes 100 fois
# 3. Si tu oublies un serveur? Erreur!
# 4. Si tu veux changer la config? Refaire 100 fois!
# = CAUCHEMAR! C'est long, répétitif, sujet aux erreurs

# PUPPET = Outil d'automatisation qui fait tout ça pour toi!
# Tu ne fais que:
# 1. Écrire UNE FOIS comment le serveur doit être configuré
# 2. Puppet applique cette config sur TOUS les serveurs
# 3. Puppet vérifie régulièrement que tout est conforme
# 4. Si quelque chose change? Puppet le corrige automatiquement!

# Puppet s'appelle un "Configuration Management Tool"
# = Outil qui gère la configuration de tes machines automatiquement

# Autres outils similaires: Ansible, Chef, SaltStack
# Mais Puppet est un des plus puissants et populaires


# === COMMENT ÇA MARCHE? (ARCHITECTURE) ===

# Puppet utilise une architecture "Agent-Server" (ou "Master-Agent")

# PUPPET SERVER (ou Puppet Master)
# = Le "cerveau" central
# = Contient toutes les configurations (appelées "manifests")
# = Dit aux agents ce qu'ils doivent faire
# = Un seul serveur pour gérer des milliers de machines!

# PUPPET AGENT
# = Installé sur chaque machine que tu veux gérer
# = Se connecte régulièrement au Puppet Server
# = Demande: "Quelle est ma configuration?"
# = Applique la configuration reçue
# = Vérifie toutes les 30 minutes (par défaut)

# Analogie: Imagine une école
# - Puppet Server = Le directeur qui définit les règles
# - Puppet Agents = Les élèves qui suivent les règles
# - Manifests = Le règlement intérieur
# - Toutes les 30 min, les élèves vérifient s'ils suivent bien les règles

# Flux de travail:
# 1. Tu écris un "manifest" (fichier .pp) sur le Puppet Server
#    Exemple: "Tous les serveurs web doivent avoir Apache installé"
#
# 2. Puppet Agent (sur chaque serveur) contacte le Puppet Server
#    Agent: "Salut Server, quelle est ma config?"
#
# 3. Puppet Server répond avec le "catalog"
#    Server: "Tu dois avoir Apache, créer l'utilisateur webadmin, etc."
#
# 4. Puppet Agent applique le catalog
#    Agent: "OK, j'installe Apache, je crée l'utilisateur..."
#
# 5. Puppet Agent envoie un rapport
#    Agent: "C'est fait! Voici ce que j'ai changé"
#
# 6. 30 minutes plus tard, ça recommence (vérification continue)


# === MODE STANDALONE (SANS SERVEUR) ===

# Tu peux aussi utiliser Puppet en mode "standalone"
# = Pas de Puppet Server, juste l'agent qui applique des manifests locaux
# = Parfait pour débuter ou gérer une seule machine

# Avantages:
# - Plus simple à installer
# - Pas besoin de gérer un serveur central
# - Parfait pour apprendre

# Inconvénients:
# - Pas centralisé (difficile pour gérer 100 serveurs)
# - Pas de rapports centralisés
# - Tu dois copier les manifests sur chaque machine

# Pour ce guide, on va commencer en mode standalone
# Puis on verra le mode agent-server plus tard


# === VOCABULAIRE PUPPET (TRÈS IMPORTANT!) ===

# MANIFEST (Fichier .pp)
# = Fichier qui contient le code Puppet
# = Extension: .pp (Puppet Program)
# = Écrit dans le langage Puppet (basé sur Ruby)
# = Dit: "Voici comment le système DOIT être configuré"
# Exemple: site.pp, apache.pp, users.pp

# RESOURCE (Ressource)
# = Un élément que Puppet peut gérer
# = Exemples: un fichier, un package, un service, un utilisateur
# = La brique de base de Puppet
# Syntaxe:
#   type { 'nom':
#     attribut => valeur,
#   }
# Exemple: 
#   file { '/etc/motd':
#     content => 'Bienvenue sur mon serveur!',
#   }

# CLASS (Classe)
# = Un groupe de ressources liées
# = Permet de réutiliser du code
# = Comme une fonction en programmation
# Exemple: une classe "apache" qui installe et configure Apache

# MODULE (Module)
# = Un ensemble de classes, fichiers, templates
# = Structure organisée pour gérer un composant complet
# = Exemples: module "apache", module "mysql", module "users"
# = Réutilisable et partageable (Puppet Forge)

# NODE (Nœud)
# = Une machine gérée par Puppet
# = Chaque agent est un "node"
# = Tu peux définir des configs différentes par node
# Exemple: "Le node 'webserver1' doit avoir Apache"

# CATALOG (Catalogue)
# = La liste complète des ressources pour un node
# = Généré par le Puppet Server
# = L'agent applique le catalog
# = C'est la "to-do list" de l'agent

# FACT (Fait)
# = Information sur un système
# = Collecté automatiquement par Puppet
# = Exemples: OS, hostname, IP, RAM, CPU
# = Utilisable dans les manifests pour des configs conditionnelles
# Exemple: "Si l'OS est Ubuntu, installe apache2; si CentOS, installe httpd"

# HIERA
# = Système de gestion des données (comme une base de données)
# = Sépare les données du code
# = Permet de stocker des variables, mots de passe, configs
# = Format: YAML ou JSON

# FACTER
# = Outil qui collecte les "facts" sur le système
# = S'exécute automatiquement avant chaque run Puppet
# = Tu peux créer des facts personnalisés

# PUPPET FORGE
# = "App Store" de Puppet
# = Bibliothèque de modules prêts à l'emploi
# = Gratuit et open-source
# = URL: https://forge.puppet.com
# Exemple: module "puppetlabs-apache" pour gérer Apache


# === LANGAGE PUPPET (INTRODUCTION) ===

# Puppet utilise son propre langage déclaratif
# = Tu dis "ce qui DOIT être" (pas "comment faire")

# Exemple incorrect (impératif):
# "Exécute apt-get update, puis apt-get install apache2"

# Exemple correct (déclaratif):
# "Le package apache2 DOIT être installé"
# Puppet décide comment le faire (apt-get, yum, etc.)

# Philosophie "Idempotence"
# = Appliquer la même config 10 fois = même résultat
# = Si Apache est déjà installé, Puppet ne fait rien
# = Pas d'effets de bord!

# Syntaxe de base:
type { 'title':
  attribute => value,
  attribute => value,
}

# Exemple concret:
package { 'apache2':
  ensure => installed,
}

# Explication:
# - package = type de ressource (un logiciel à installer)
# - 'apache2' = nom du package
# - ensure => installed = l'attribut qui dit "doit être installé"


# === POURQUOI UTILISER PUPPET? ===

# Avantages de Puppet:

# 1. AUTOMATISATION
# - Élimine les tâches répétitives
# - Gagne des heures de travail manuel
# - Exemple: installer un serveur en 5 minutes au lieu de 2 heures

# 2. CONSISTANCE
# - Tous les serveurs sont identiques
# - Pas d'erreur humaine (oubli, faute de frappe)
# - Configuration standardisée

# 3. SELF-HEALING (AUTO-RÉPARATION)
# - Si quelqu'un modifie un fichier manuellement
# - Puppet le remet dans l'état correct
# - Détecte les dérives de configuration

# 4. INFRASTRUCTURE AS CODE
# - Toute ta config est dans des fichiers
# - Versionnable avec Git
# - Documenté et reproductible

# 5. SCALABILITÉ
# - Gère facilement 10, 100, ou 10,000 serveurs
# - Même effort pour 1 ou 1000 machines

# 6. COMPLIANCE
# - S'assure que les serveurs respectent les normes de sécurité
# - Auditable (logs, rapports)

# Cas d'usage réels:
# - DevOps: Déploiement d'applications
# - SysAdmin: Gestion de serveurs
# - Sécurité: Application de patches, configurations sécurisées
# - Cloud: Configuration de VMs (AWS, Azure, GCP)


[OK] INSTALLATION SUPER DÉTAILLÉE

# === PRÉREQUIS ===

# Avant d'installer Puppet, tu as besoin de:
# 1. Un système Linux, macOS, ou Windows
#    (Puppet fonctionne sur tous les OS!)
# 2. Accès root/sudo (pour installer des packages)
# 3. Connexion internet (pour télécharger Puppet)

# Versions de Puppet:
# - Puppet 7 (dernière version, 2021+)
# - Puppet 6 (stable, supporté jusqu'en 2023)
# - Puppet 5 (ancien, éviter)

# Pour ce guide, on va installer Puppet 7


# === ÉTAPE 1: VÉRIFIER TON SYSTÈME ===

# Voir l'OS et la version:
cat /etc/os-release
# Affiche:
# NAME="Ubuntu"
# VERSION="22.04 LTS (Jammy Jellyfish)"

# Ou sur CentOS/RedHat:
cat /etc/redhat-release

# Ou sur macOS:
sw_vers

# Mettre à jour le système:
# Ubuntu/Debian:
sudo apt update && sudo apt upgrade -y

# CentOS/RedHat:
sudo yum update -y


# === INSTALLATION SELON TON OS ===

# ========== UBUNTU / DEBIAN ==========

# === ÉTAPE 2: AJOUTER LE DÉPÔT PUPPET ===

# Puppet n'est pas dans les dépôts par défaut
# On doit ajouter le dépôt officiel Puppet

# Télécharger le package du dépôt:
wget https://apt.puppet.com/puppet7-release-jammy.deb
# "jammy" = Ubuntu 22.04 (change selon ta version!)
# Ubuntu 20.04 = focal
# Ubuntu 18.04 = bionic

# Installer le dépôt:
sudo dpkg -i puppet7-release-jammy.deb

# Mettre à jour les sources:
sudo apt update


# === ÉTAPE 3: INSTALLER PUPPET AGENT ===

# Pour mode standalone (débuter):
sudo apt install puppet-agent -y

# Vérifier l'installation:
/opt/puppetlabs/bin/puppet --version
# Affiche: 7.X.X

# Ajouter Puppet au PATH (pour ne pas taper le chemin complet):
echo 'export PATH=/opt/puppetlabs/bin:$PATH' >> ~/.bashrc
source ~/.bashrc

# Maintenant tu peux faire:
puppet --version


# ========== CENTOS / REDHAT ==========

# === ÉTAPE 2: AJOUTER LE DÉPÔT PUPPET ===

# Télécharger le package du dépôt:
sudo rpm -Uvh https://yum.puppet.com/puppet7-release-el-8.noarch.rpm
# "el-8" = RHEL/CentOS 8 (change selon ta version!)
# RHEL 7 = el-7

# Mettre à jour:
sudo yum update -y


# === ÉTAPE 3: INSTALLER PUPPET AGENT ===

sudo yum install puppet-agent -y

# Vérifier:
/opt/puppetlabs/bin/puppet --version

# Ajouter au PATH:
echo 'export PATH=/opt/puppetlabs/bin:$PATH' >> ~/.bashrc
source ~/.bashrc


# ========== macOS ==========

# === ÉTAPE 2: TÉLÉCHARGER L'INSTALLEUR ===

# Va sur: https://puppet.com/try-puppet/puppet-open-source/
# Télécharge le fichier .dmg pour macOS

# Ou avec la ligne de commande:
curl -O https://downloads.puppetlabs.com/mac/puppet7/puppet-agent-latest.dmg

# Double-clique sur le fichier .dmg
# Suis les instructions d'installation


# === ÉTAPE 3: VÉRIFIER ===

puppet --version


# ========== WINDOWS ==========

# === ÉTAPE 2: TÉLÉCHARGER L'INSTALLEUR ===

# Va sur: https://puppet.com/try-puppet/puppet-open-source/
# Télécharge le fichier .msi pour Windows

# Double-clique sur le fichier .msi
# Clique "Suivant" plusieurs fois


# === ÉTAPE 3: VÉRIFIER ===

# Ouvre PowerShell en tant qu'administrateur
puppet --version


# === ÉTAPE 4: VÉRIFIER L'INSTALLATION (TOUS OS) ===

# Commandes de base pour vérifier:

# Version de Puppet:
puppet --version
# Affiche: 7.X.X

# Aide générale:
puppet help

# Liste des commandes disponibles:
puppet help | grep "^  "
# Affiche:
#   agent        The puppet agent daemon
#   apply        Apply Puppet manifests locally
#   config       Interact with Puppet's settings
#   resource     Interact with system resources


# === ÉTAPE 5: CONFIGURER PUPPET (OPTIONNEL POUR DÉBUTER) ===

# Fichier de configuration principal:
# /etc/puppetlabs/puppet/puppet.conf

# Voir la config actuelle:
puppet config print

# Affiche plein de paramètres:
# certname = mon-ordinateur.local
# server = puppet
# runinterval = 1800  (30 minutes)

# Éditer la config:
sudo nano /etc/puppetlabs/puppet/puppet.conf
# ou
sudo vim /etc/puppetlabs/puppet/puppet.conf

# Exemple de config minimale:
[main]
certname = mon-ordinateur
server = puppet.example.com  (si tu utilises un serveur)
runinterval = 1800

[agent]
environment = production


# === ÉTAPE 6: TESTER PUPPET EN MODE STANDALONE ===

# Créer un manifest de test:
sudo mkdir -p /etc/puppetlabs/code/environments/production/manifests
sudo nano /etc/puppetlabs/code/environments/production/manifests/test.pp

# Contenu du fichier test.pp:
file { '/tmp/puppet-test.txt':
  ensure  => present,
  content => "Puppet fonctionne! Créé le $(date)\n",
}

# Appliquer le manifest:
sudo puppet apply /etc/puppetlabs/code/environments/production/manifests/test.pp

# Affiche:
# Notice: Compiled catalog for mon-ordinateur in environment production in 0.05 seconds
# Notice: /Stage[main]/Main/File[/tmp/puppet-test.txt]/ensure: created
# Notice: Applied catalog in 0.01 seconds

# Vérifier que le fichier a été créé:
cat /tmp/puppet-test.txt
# Affiche: Puppet fonctionne! Créé le ...

# BRAVO! Puppet fonctionne!


[OK] STRUCTURE DE PROJET (EXPLICATIONS TRÈS DÉTAILLÉES)

# === ARBORESCENCE PUPPET (TRÈS IMPORTANT!) ===

# Puppet utilise une structure de fichiers très spécifique
# C'est comme un plan de maison: chaque chose a sa place!

# Structure complète:
/etc/puppetlabs/
├── code/                      # Tout ton code Puppet
│   ├── environments/          # Environnements (production, dev, test)
│   │   ├── production/        # Environnement de production
│   │   │   ├── manifests/     # Fichiers .pp (manifests)
│   │   │   │   └── site.pp    # Fichier principal
│   │   │   ├── modules/       # Tes modules personnalisés
│   │   │   │   ├── apache/
│   │   │   │   ├── mysql/
│   │   │   │   └── users/
│   │   │   ├── data/          # Données Hiera (YAML)
│   │   │   │   └── common.yaml
│   │   │   ├── hiera.yaml     # Config Hiera
│   │   │   └── environment.conf
│   │   └── development/       # Environnement de dev
│   └── modules/               # Modules globaux (partagés)
├── puppet/
│   ├── puppet.conf            # Configuration Puppet
│   ├── ssl/                   # Certificats SSL
│   └── hiera.yaml             # Config Hiera globale
└── facter/
    └── facts.d/               # Facts personnalisés

# Explications détaillées:

# === /etc/puppetlabs/code/ ===
# = Répertoire racine de tout ton code
# = TOUS tes manifests, modules, données vont ici
# = Ne JAMAIS mettre de code ailleurs!

# === /etc/puppetlabs/code/environments/ ===
# = Contient les différents environnements
# = Environnement = Version de ton code
# Exemple d'usage:
#   - production: Code stable utilisé en prod
#   - development: Code en cours de développement
#   - testing: Code pour tester avant prod
# = Permet de tester sans casser la prod!

# === /etc/puppetlabs/code/environments/production/manifests/ ===
# = Dossier des manifests (.pp)
# = Contient le code Puppet
# = Le fichier principal s'appelle TOUJOURS site.pp

# === site.pp (LE FICHIER LE PLUS IMPORTANT!) ===
# = Point d'entrée de Puppet
# = Puppet lit TOUJOURS ce fichier en premier
# = Contient les définitions de nodes
# = C'est comme le main() en programmation

# Exemple de site.pp:
# /etc/puppetlabs/code/environments/production/manifests/site.pp

# Configuration par défaut (pour tous les nodes)
node default {
  # Code ici s'applique à TOUS les serveurs
  file { '/etc/motd':
    content => "Bienvenue! Ce serveur est géré par Puppet.\n",
  }
}

# Configuration spécifique pour un node
node 'webserver1.example.com' {
  # Code ici s'applique SEULEMENT à webserver1
  include apache
  include mysql
}

# Configuration pour plusieurs nodes similaires
node /^web\d+\.example\.com$/ {
  # Regex: web1, web2, web3, ... web99
  include apache
}

# === /etc/puppetlabs/code/environments/production/modules/ ===
# = Dossier des modules
# = Module = Composant réutilisable (apache, mysql, etc.)
# = Chaque module a sa propre structure

# Structure d'un module:
modules/
└── apache/
    ├── manifests/        # Code Puppet du module
    │   ├── init.pp       # Classe principale (obligatoire!)
    │   ├── install.pp    # Sous-classe pour l'installation
    │   ├── config.pp     # Sous-classe pour la configuration
    │   └── service.pp    # Sous-classe pour le service
    ├── files/            # Fichiers statiques
    │   └── httpd.conf
    ├── templates/        # Templates (ERB, EPP)
    │   └── vhost.conf.erb
    ├── lib/              # Code Ruby personnalisé
    ├── facts.d/          # Facts personnalisés
    ├── examples/         # Exemples d'utilisation
    ├── spec/             # Tests
    └── metadata.json     # Métadonnées du module

# === /etc/puppetlabs/code/environments/production/data/ ===
# = Données Hiera (séparation code/données)
# = Format YAML ou JSON
# = Contient les variables, mots de passe, configs

# Exemple: data/common.yaml
---
# Variables globales
apache::port: 80
mysql::root_password: 'changeme'
ntp_servers:
  - 0.pool.ntp.org
  - 1.pool.ntp.org

# === /etc/puppetlabs/puppet/puppet.conf ===
# = Fichier de configuration principal
# = Paramètres globaux de Puppet

# Exemple de puppet.conf:
[main]
# Nom du certificat (identité du node)
certname = mon-serveur.example.com

# Environnement par défaut
environment = production

# Intervalle entre les runs (30 minutes)
runinterval = 1800

[agent]
# Adresse du Puppet Server
server = puppet.example.com

# Rapport à envoyer au serveur
report = true

[server]
# Port du Puppet Server
masterport = 8140


# === CRÉER UN MODULE COMPLET (EXEMPLE) ===

# On va créer un module "nginx" de A à Z

# Étape 1: Créer la structure
sudo mkdir -p /etc/puppetlabs/code/environments/production/modules/nginx/{manifests,files,templates}

# Étape 2: Créer le manifest principal (init.pp)
sudo nano /etc/puppetlabs/code/environments/production/modules/nginx/manifests/init.pp

# Contenu:
class nginx {
  # Inclure les sous-classes
  include nginx::install
  include nginx::config
  include nginx::service

  # Ordre d'exécution
  Class['nginx::install']
  -> Class['nginx::config']
  ~> Class['nginx::service']
}

# Explications:
# class nginx { ... } = Définit la classe principale
# include = Inclut d'autres classes
# -> = "puis" (ordre d'exécution)
# ~> = "puis, et notifie si changement"

# Étape 3: Créer install.pp
sudo nano /etc/puppetlabs/code/environments/production/modules/nginx/manifests/install.pp

class nginx::install {
  package { 'nginx':
    ensure => installed,
  }
}

# Étape 4: Créer config.pp
sudo nano /etc/puppetlabs/code/environments/production/modules/nginx/manifests/config.pp

class nginx::config {
  file { '/etc/nginx/nginx.conf':
    ensure  => present,
    source  => 'puppet:///modules/nginx/nginx.conf',
    require => Package['nginx'],
  }
}

# Étape 5: Créer service.pp
sudo nano /etc/puppetlabs/code/environments/production/modules/nginx/manifests/service.pp

class nginx::service {
  service { 'nginx':
    ensure     => running,
    enable     => true,
    hasstatus  => true,
    hasrestart => true,
  }
}

# Étape 6: Créer le fichier de config
sudo nano /etc/puppetlabs/code/environments/production/modules/nginx/files/nginx.conf

# Copie ta config nginx ici

# Étape 7: Utiliser le module dans site.pp
sudo nano /etc/puppetlabs/code/environments/production/manifests/site.pp

node default {
  include nginx
}

# Étape 8: Appliquer
sudo puppet apply /etc/puppetlabs/code/environments/production/manifests/site.pp

# BRAVO! Nginx est installé, configuré, et démarré!


[OK] SYNTAXE PUPPET (EXPLICATIONS TRÈS DÉTAILLÉES)

# === TYPES DE RESSOURCES (RESOURCE TYPES) ===

# Puppet gère différents types de ressources
# Chaque type a ses propres attributs

# === TYPE #1: PACKAGE (Gestion des packages) ===

# Syntaxe générale:
package { 'nom-du-package':
  ensure => <état>,
}

# États possibles pour ensure:
# - installed / present: Package doit être installé
# - absent: Package ne doit PAS être installé
# - latest: Installer la dernière version
# - '1.2.3': Installer une version spécifique

# Exemples:

# Installer un package:
package { 'apache2':
  ensure => installed,
}

# Désinstaller un package:
package { 'telnet':
  ensure => absent,
}

# Installer la dernière version:
package { 'nginx':
  ensure => latest,
}

# Installer une version spécifique:
package { 'docker-ce':
  ensure => '20.10.7',
}

# Installer plusieurs packages:
package { ['git', 'curl', 'vim']:
  ensure => installed,
}

# Attributs supplémentaires:

# provider: Spécifier le gestionnaire de packages
package { 'python-pip':
  ensure   => installed,
  provider => 'pip',
}
# Providers: apt, yum, pip, gem, npm, chocolatey (Windows)


# === TYPE #2: FILE (Gestion des fichiers) ===

# Syntaxe générale:
file { 'chemin-du-fichier':
  ensure  => <type>,
  content => <contenu>,
  source  => <source>,
  mode    => '0644',
  owner   => 'root',
  group   => 'root',
}

# Types possibles pour ensure:
# - present / file: Fichier doit exister
# - absent: Fichier ne doit PAS exister
# - directory: Doit être un dossier
# - link: Doit être un lien symbolique

# Exemples:

# Créer un fichier avec du contenu:
file { '/etc/motd':
  ensure  => present,
  content => "Bienvenue sur mon serveur!\n",
  mode    => '0644',
  owner   => 'root',
  group   => 'root',
}

# Créer un fichier depuis une source:
file { '/etc/nginx/nginx.conf':
  ensure => present,
  source => 'puppet:///modules/nginx/nginx.conf',
  mode   => '0644',
}

# Supprimer un fichier:
file { '/tmp/old-file.txt':
  ensure => absent,
}

# Créer un dossier:
file { '/var/www/myapp':
  ensure => directory,
  mode   => '0755',
  owner  => 'www-data',
  group  => 'www-data',
}

# Créer un lien symbolique:
file { '/usr/bin/python':
  ensure => link,
  target => '/usr/bin/python3',
}

# Créer un fichier depuis un template:
file { '/etc/apache2/sites-available/mysite.conf':
  ensure  => present,
  content => template('apache/vhost.conf.erb'),
  mode    => '0644',
}

# Récursif (copier tout un dossier):
file { '/var/www/html':
  ensure  => directory,
  source  => 'puppet:///modules/website/html',
  recurse => true,
}

# Attributs supplémentaires:

# replace: Remplacer le fichier si différent
file { '/etc/config.ini':
  ensure  => present,
  content => "key=value\n",
  replace => false,  # Ne pas écraser si existe déjà
}

# backup: Créer une sauvegarde avant modification
file { '/etc/important.conf':
  ensure  => present,
  content => "new config",
  backup  => '.bak',  # Crée .bak avant modification
}


# === TYPE #3: SERVICE (Gestion des services) ===

# Syntaxe générale:
service { 'nom-du-service':
  ensure     => <état>,
  enable     => <bool>,
  hasstatus  => true,
  hasrestart => true,
}

# États possibles pour ensure:
# - running: Service doit être démarré
# - stopped: Service doit être arrêté

# enable:
# - true: Service démarre au boot
# - false: Service ne démarre pas au boot

# Exemples:

# Démarrer et activer un service:
service { 'apache2':
  ensure     => running,
  enable     => true,
  hasstatus  => true,
  hasrestart => true,
}

# Arrêter et désactiver un service:
service { 'bluetooth':
  ensure => stopped,
  enable => false,
}

# Redémarrer si config change (notify):
file { '/etc/nginx/nginx.conf':
  ensure  => present,
  content => template('nginx/nginx.conf.erb'),
  notify  => Service['nginx'],  # Redémarre nginx si fichier change
}

service { 'nginx':
  ensure     => running,
  enable     => true,
  hasstatus  => true,
  hasrestart => true,
}

# subscribe: Écoute les changements (inverse de notify)
service { 'apache2':
  ensure    => running,
  subscribe => File['/etc/apache2/apache2.conf'],
}


# === TYPE #4: USER (Gestion des utilisateurs) ===

# Syntaxe générale:
user { 'nom-utilisateur':
  ensure     => present,
  uid        => 1001,
  gid        => 1001,
  shell      => '/bin/bash',
  home       => '/home/nom',
  managehome => true,
}

# Exemples:

# Créer un utilisateur:
user { 'john':
  ensure     => present,
  uid        => 1001,
  gid        => 'users',
  shell      => '/bin/bash',
  home       => '/home/john',
  managehome => true,  # Créer le home automatiquement
  comment    => 'John Doe',
}

# Créer un utilisateur système (pas de home):
user { 'nginx':
  ensure => present,
  system => true,
  shell  => '/sbin/nologin',
}

# Supprimer un utilisateur:
user { 'old-user':
  ensure => absent,
}

# Changer le mot de passe (hash SHA-512):
user { 'admin':
  ensure   => present,
  password => '$6$rounds=656000$YourSaltHere$HashedPasswordHere',
}

# Générer un hash de mot de passe:
# Linux:
openssl passwd -6 'mon-mot-de-passe'
# Affiche: $6$...


# === TYPE #5: GROUP (Gestion des groupes) ===

# Syntaxe générale:
group { 'nom-groupe':
  ensure => present,
  gid    => 1001,
}

# Exemples:

# Créer un groupe:
group { 'developers':
  ensure => present,
  gid    => 2000,
}

# Supprimer un groupe:
group { 'old-group':
  ensure => absent,
}

# Ajouter des membres à un groupe:
group { 'wheel':
  ensure  => present,
  members => ['admin', 'john', 'jane'],
}


# === TYPE #6: EXEC (Exécution de commandes) ===

# Syntaxe générale:
exec { 'nom-unique':
  command => 'commande à exécuter',
  path    => ['/usr/bin', '/bin'],
  creates => '/fichier/créé',
  unless  => 'commande de test',
  onlyif  => 'commande de test',
}

# ATTENTION: exec doit être idempotent!
# Utilise creates, unless, ou onlyif pour éviter de réexécuter

# Exemples:

# Exécuter une commande UNE SEULE FOIS:
exec { 'install-docker':
  command => 'curl -fsSL https://get.docker.com | sh',
  path    => ['/usr/bin', '/bin'],
  creates => '/usr/bin/docker',  # Ne réexécute pas si docker existe
}

# Exécuter seulement si une condition est fausse:
exec { 'create-database':
  command => 'mysql -e "CREATE DATABASE mydb"',
  path    => ['/usr/bin'],
  unless  => 'mysql -e "SHOW DATABASES" | grep mydb',
}

# Exécuter seulement si une condition est vraie:
exec { 'restart-service':
  command => 'systemctl restart myservice',
  path    => ['/bin', '/usr/bin'],
  onlyif  => 'systemctl is-active myservice',
}

# Exécuter après un autre ressource (refreshonly):
exec { 'reload-nginx':
  command     => 'nginx -s reload',
  path        => ['/usr/sbin'],
  refreshonly => true,  # Exécute seulement si notifié
}

file { '/etc/nginx/nginx.conf':
  ensure  => present,
  content => template('nginx/nginx.conf.erb'),
  notify  => Exec['reload-nginx'],  # Notifie l'exec si change
}


# === TYPE #7: CRON (Gestion des tâches cron) ===

# Syntaxe générale:
cron { 'nom-unique':
  command => 'commande à exécuter',
  user    => 'root',
  hour    => '2',
  minute  => '0',
  weekday => '*',
}

# Exemples:

# Backup quotidien à 2h du matin:
cron { 'daily-backup':
  command => '/usr/local/bin/backup.sh',
  user    => 'root',
  hour    => '2',
  minute  => '0',
}

# Nettoyage toutes les heures:
cron { 'cleanup-tmp':
  command => 'find /tmp -type f -mtime +7 -delete',
  user    => 'root',
  minute  => '0',
}

# Tâche toutes les 5 minutes:
cron { 'check-health':
  command => '/usr/local/bin/health-check.sh',
  user    => 'monitoring',
  minute  => '*/5',
}

# Tâche le lundi à 9h:
cron { 'weekly-report':
  command => '/usr/local/bin/report.sh',
  user    => 'admin',
  hour    => '9',
  minute  => '0',
  weekday => '1',  # 1 = lundi
}

# Supprimer une tâche cron:
cron { 'old-job':
  ensure => absent,
}


# === RELATIONS ENTRE RESSOURCES ===

# Puppet peut gérer l'ordre d'exécution des ressources

# === OPÉRATEUR -> (Ordering) ===
# A -> B = "A doit s'exécuter AVANT B"

package { 'apache2':
  ensure => installed,
}
->
service { 'apache2':
  ensure => running,
}

# Équivalent à:
package { 'apache2':
  ensure => installed,
  before => Service['apache2'],
}

service { 'apache2':
  ensure  => running,
  require => Package['apache2'],
}


# === OPÉRATEUR ~> (Notification) ===
# A ~> B = "Si A change, notifie B (redémarre, recharge...)"

file { '/etc/nginx/nginx.conf':
  ensure  => present,
  content => template('nginx/nginx.conf.erb'),
}
~>
service { 'nginx':
  ensure => running,
}

# Équivalent à:
file { '/etc/nginx/nginx.conf':
  ensure  => present,
  content => template('nginx/nginx.conf.erb'),
  notify  => Service['nginx'],
}


# === ATTRIBUT require ===
# "Cette ressource nécessite une autre ressource"

file { '/var/www/html/index.html':
  ensure  => present,
  content => '<h1>Hello</h1>',
  require => Package['apache2'],  # Installer Apache d'abord
}


# === ATTRIBUT before ===
# "Cette ressource doit s'exécuter avant une autre"

package { 'mysql-server':
  ensure => installed,
  before => Service['mysql'],  # Avant de démarrer le service
}


# === ATTRIBUT notify ===
# "Notifier une autre ressource si changement"

file { '/etc/ssh/sshd_config':
  ensure  => present,
  content => template('ssh/sshd_config.erb'),
  notify  => Service['sshd'],  # Redémarre sshd si config change
}


# === ATTRIBUT subscribe ===
# "Écouter les changements d'une autre ressource"

service { 'apache2':
  ensure    => running,
  subscribe => File['/etc/apache2/apache2.conf'],
}


# === VARIABLES ===

# Puppet supporte les variables

# Déclaration:
$my_var = 'valeur'
$port = 8080
$enable_ssl = true

# Utilisation:
file { '/etc/config.txt':
  ensure  => present,
  content => "Port: ${port}\n",
}

# Variables de facts (système):
$os_name = $facts['os']['name']
$ip_address = $facts['networking']['ip']

file { '/etc/motd':
  ensure  => present,
  content => "OS: ${os_name}\nIP: ${ip_address}\n",
}


# === CONDITIONNELLES ===

# === IF / ELSE ===

if $facts['os']['family'] == 'Debian' {
  package { 'apache2':
    ensure => installed,
  }
} elsif $facts['os']['family'] == 'RedHat' {
  package { 'httpd':
    ensure => installed,
  }
} else {
  fail('OS non supporté')
}


# === CASE ===

case $facts['os']['name'] {
  'Ubuntu': {
    $package_name = 'apache2'
  }
  'CentOS': {
    $package_name = 'httpd'
  }
  default: {
    fail("OS ${facts['os']['name']} non supporté")
  }
}

package { $package_name:
  ensure => installed,
}


# === SELECTOR ===

$package = $facts['os']['family'] ? {
  'Debian' => 'apache2',
  'RedHat' => 'httpd',
  default  => 'httpd',
}

package { $package:
  ensure => installed,
}


# === BOUCLES ===

# === EACH ===

$packages = ['git', 'vim', 'curl', 'htop']

each($packages) |$pkg| {
  package { $pkg:
    ensure => installed,
  }
}


# === ITERATION SUR UN HASH ===

$users = {
  'john' => { uid => 1001, shell => '/bin/bash' },
  'jane' => { uid => 1002, shell => '/bin/zsh' },
}

$users.each |$username, $attrs| {
  user { $username:
    ensure => present,
    uid    => $attrs['uid'],
    shell  => $attrs['shell'],
  }
}


# === FONCTIONS ===

# Puppet a plein de fonctions intégrées

# === template() ===
# Charge un template ERB
$content = template('mymodule/config.erb')

# === file() ===
# Lit le contenu d'un fichier
$ssh_key = file('/root/.ssh/id_rsa.pub')

# === lookup() ===
# Cherche une valeur dans Hiera
$database_password = lookup('mysql::root_password')

# === fail() ===
# Arrête l'exécution avec un message d'erreur
if $facts['os']['family'] == 'Windows' {
  fail('Ce manifest ne fonctionne pas sur Windows')
}

# === notice() / warning() / err() ===
# Affiche des messages
notice('Installation en cours...')
warning('Cette config est dépréciée')
err('Erreur critique!')


[OK] TEMPLATES (EXPLICATIONS TRÈS DÉTAILLÉES)

# === QU'EST-CE QU'UN TEMPLATE? ===

# Un template = Fichier avec des variables dynamiques
# Permet de générer des fichiers de config personnalisés

# Exemples d'usage:
# - Fichier de config Apache avec le nom du serveur
# - Fichier de config MySQL avec le mot de passe
# - Fichier de config réseau avec l'IP

# Puppet supporte 2 formats de templates:
# 1. ERB (Embedded Ruby) - format classique
# 2. EPP (Embedded Puppet) - format moderne


# === TEMPLATE ERB (EMBEDDED RUBY) ===

# Extension: .erb
# Syntaxe: <%= variable %> pour afficher une variable
#          <% code %> pour du code Ruby

# Exemple de template ERB:

# modules/apache/templates/vhost.conf.erb
<VirtualHost *:<%= @port %>>
  ServerName <%= @server_name %>
  DocumentRoot <%= @docroot %>
  
  <Directory <%= @docroot %>>
    AllowOverride All
    Require all granted
  </Directory>
  
  <% if @enable_ssl %>
  SSLEngine on
  SSLCertificateFile <%= @ssl_cert %>
  SSLCertificateKeyFile <%= @ssl_key %>
  <% end %>
  
  ErrorLog ${APACHE_LOG_DIR}/<%= @server_name %>-error.log
  CustomLog ${APACHE_LOG_DIR}/<%= @server_name %>-access.log combined
</VirtualHost>

# Utilisation dans un manifest:

class apache::vhost (
  String $server_name,
  String $docroot,
  Integer $port = 80,
  Boolean $enable_ssl = false,
  String $ssl_cert = '',
  String $ssl_key = '',
) {
  file { "/etc/apache2/sites-available/${server_name}.conf":
    ensure  => present,
    content => template('apache/vhost.conf.erb'),
    notify  => Service['apache2'],
  }
}

# Appel de la classe:
apache::vhost { 'mysite':
  server_name => 'www.example.com',
  docroot     => '/var/www/mysite',
  port        => 443,
  enable_ssl  => true,
  ssl_cert    => '/etc/ssl/certs/example.com.crt',
  ssl_key     => '/etc/ssl/private/example.com.key',
}


# === TEMPLATE EPP (EMBEDDED PUPPET) ===

# Extension: .epp
# Syntaxe: <%= $variable %> pour afficher une variable
#          <% code %> pour du code Puppet

# Exemple de template EPP:

# modules/nginx/templates/nginx.conf.epp
user <%= $user %>;
worker_processes <%= $worker_processes %>;
pid /run/nginx.pid;

events {
  worker_connections <%= $worker_connections %>;
}

http {
  sendfile on;
  tcp_nopush on;
  tcp_nodelay on;
  
  <% if $enable_gzip { %>
  gzip on;
  gzip_vary on;
  gzip_types text/plain text/css application/json;
  <% } %>
  
  include /etc/nginx/mime.types;
  default_type application/octet-stream;
  
  access_log <%= $access_log %>;
  error_log <%= $error_log %>;
  
  include /etc/nginx/conf.d/*.conf;
  include /etc/nginx/sites-enabled/*;
}

# Utilisation dans un manifest:

class nginx::config (
  String $user = 'www-data',
  Integer $worker_processes = 4,
  Integer $worker_connections = 1024,
  Boolean $enable_gzip = true,
  String $access_log = '/var/log/nginx/access.log',
  String $error_log = '/var/log/nginx/error.log',
) {
  file { '/etc/nginx/nginx.conf':
    ensure  => present,
    content => epp('nginx/nginx.conf.epp', {
      'user'                => $user,
      'worker_processes'    => $worker_processes,
      'worker_connections'  => $worker_connections,
      'enable_gzip'         => $enable_gzip,
      'access_log'          => $access_log,
      'error_log'           => $error_log,
    }),
    notify  => Service['nginx'],
  }
}


# === FONCTIONS DANS LES TEMPLATES ===

# ERB supporte du code Ruby:

# Template: config.erb
# Configuration générée le <%= Time.now.strftime('%Y-%m-%d %H:%M:%S') %>
#
# Serveurs:
<% @servers.each do |server| %>
  - <%= server %>
<% end %>

# Manifest:
$servers = ['web1.example.com', 'web2.example.com', 'web3.example.com']

file { '/etc/servers.conf':
  ensure  => present,
  content => template('mymodule/config.erb'),
}


# EPP supporte du code Puppet:

# Template: config.epp
# Configuration générée le <%= timestamp() %>
#
# Serveurs:
<% $servers.each |$server| { %>
  - <%= $server %>
<% } %>


[OK] HIERA (GESTION DES DONNÉES)

# === QU'EST-CE QUE HIERA? ===

# Hiera = Système de gestion des données hiérarchiques
# Prononciation: "High-era" ou "Hiera" (comme "hier")

# Problème sans Hiera:
# - Les données (mots de passe, IPs, ports) sont mélangées avec le code
# - Difficile de gérer des configs différentes par environnement
# - Pas de séparation code/données

# Avec Hiera:
# - Les données sont dans des fichiers YAML séparés
# - Le code Puppet reste générique
# - Facile de changer des valeurs sans toucher au code

# Analogie:
# Code Puppet = Recette de cuisine (comment faire)
# Hiera = Liste d'ingrédients (quoi utiliser)


# === STRUCTURE HIERA ===

# Fichier de config principal: hiera.yaml
# Fichiers de données: YAML dans data/

# Structure:
/etc/puppetlabs/code/environments/production/
├── hiera.yaml           # Config Hiera
└── data/                # Données
    ├── common.yaml      # Données par défaut (tout le monde)
    ├── nodes/           # Données par node
    │   ├── web1.yaml
    │   └── db1.yaml
    ├── os/              # Données par OS
    │   ├── Ubuntu.yaml
    │   └── CentOS.yaml
    └── environment.yaml # Données par environnement


# === FICHIER hiera.yaml ===

# Exemple de hiera.yaml:
---
version: 5

defaults:
  datadir: data
  data_hash: yaml_data

hierarchy:
  - name: "Per-node data"
    path: "nodes/%{trusted.certname}.yaml"
  
  - name: "Per-OS data"
    path: "os/%{facts.os.family}.yaml"
  
  - name: "Common data"
    path: "common.yaml"

# Explications:
# version: 5 = Version de Hiera (toujours 5)
# datadir: data = Dossier des fichiers de données
# data_hash: yaml_data = Format YAML
# hierarchy = Ordre de recherche (de haut en bas)

# Quand Puppet cherche une valeur:
# 1. Regarde dans nodes/web1.yaml (spécifique au node)
# 2. Si pas trouvé, regarde dans os/Debian.yaml (spécifique à l'OS)
# 3. Si pas trouvé, regarde dans common.yaml (par défaut)


# === FICHIERS DE DONNÉES ===

# === data/common.yaml ===
# Données globales (tous les nodes)

---
# Packages à installer partout
common_packages:
  - vim
  - git
  - htop
  - curl

# Port SSH par défaut
ssh::port: 22

# Configuration NTP
ntp::servers:
  - 0.pool.ntp.org
  - 1.pool.ntp.org

# MySQL root password
mysql::root_password: 'changeme123'


# === data/nodes/web1.example.com.yaml ===
# Données spécifiques pour le node web1

---
# Surcharge le port SSH pour ce node
ssh::port: 2222

# Packages supplémentaires pour ce node
apache::modules:
  - rewrite
  - ssl
  - headers

# VirtualHost Apache
apache::vhosts:
  mysite:
    server_name: www.example.com
    docroot: /var/www/mysite
    port: 80


# === data/os/Debian.yaml ===
# Données spécifiques pour Debian/Ubuntu

---
# Nom du package Apache sur Debian
apache::package_name: apache2
apache::service_name: apache2

# Chemin de config
apache::config_dir: /etc/apache2


# === data/os/RedHat.yaml ===
# Données spécifiques pour RedHat/CentOS

---
# Nom du package Apache sur RedHat
apache::package_name: httpd
apache::service_name: httpd

# Chemin de config
apache::config_dir: /etc/httpd


# === UTILISER HIERA DANS LES MANIFESTS ===

# === Méthode 1: Automatic Parameter Lookup ===
# Puppet cherche automatiquement dans Hiera

class apache (
  String $package_name,
  String $service_name,
  Integer $port = 80,
) {
  package { $package_name:
    ensure => installed,
  }
  
  service { $service_name:
    ensure => running,
    enable => true,
  }
}

# Puppet cherche automatiquement:
# - apache::package_name dans Hiera
# - apache::service_name dans Hiera
# - apache::port dans Hiera (ou utilise 80 si pas trouvé)


# === Méthode 2: lookup() ===
# Cherche explicitement une valeur dans Hiera

$root_password = lookup('mysql::root_password')

$servers = lookup('ntp::servers', Array, 'first', [])
# lookup(clé, type, stratégie, défaut)

$packages = lookup('common_packages', {
  'value_type'    => Array,
  'default_value' => [],
})


# === Méthode 3: hiera() (ancien, déprécié) ===

$password = hiera('mysql::root_password')
# Utilise lookup() à la place!


# === STRATÉGIES DE MERGE ===

# Comment Hiera fusionne les valeurs trouvées

# === first (par défaut) ===
# Retourne la première valeur trouvée

$value = lookup('key', String, 'first', 'default')

# common.yaml: key: 'value1'
# nodes/web1.yaml: key: 'value2'
# Résultat pour web1: 'value2' (la plus spécifique)


# === unique ===
# Fusionne tous les tableaux sans doublons

$packages = lookup('packages', Array, 'unique', [])

# common.yaml:
packages:
  - vim
  - git

# nodes/web1.yaml:
packages:
  - apache2
  - git

# Résultat: ['vim', 'git', 'apache2'] (git n'apparaît qu'une fois)


# === hash ===
# Fusionne tous les hashs

$config = lookup('myapp::config', Hash, 'hash', {})

# common.yaml:
myapp::config:
  port: 8080
  debug: false

# nodes/web1.yaml:
myapp::config:
  host: '0.0.0.0'
  debug: true

# Résultat:
# {
#   'port'  => 8080,
#   'host'  => '0.0.0.0',
#   'debug' => true
# }


# === deep ===
# Fusionne récursivement les hashs

$config = lookup('myapp::config', Hash, 'deep', {})

# common.yaml:
myapp::config:
  database:
    host: localhost
    port: 3306
  cache:
    enabled: true

# nodes/web1.yaml:
myapp::config:
  database:
    host: 192.168.1.10
  cache:
    ttl: 3600

# Résultat:
# {
#   'database' => {
#     'host' => '192.168.1.10',  (surchargé)
#     'port' => 3306,            (gardé)
#   },
#   'cache' => {
#     'enabled' => true,         (gardé)
#     'ttl'     => 3600,         (ajouté)
#   }
# }


# === EXEMPLE COMPLET AVEC HIERA ===

# === hiera.yaml ===
---
version: 5

defaults:
  datadir: data
  data_hash: yaml_data

hierarchy:
  - name: "Per-node"
    path: "nodes/%{trusted.certname}.yaml"
  
  - name: "Per-environment"
    path: "environment/%{server_facts.environment}.yaml"
  
  - name: "Per-OS"
    path: "os/%{facts.os.family}.yaml"
  
  - name: "Common"
    path: "common.yaml"


# === data/common.yaml ===
---
# SSH
ssh::port: 22
ssh::permit_root_login: 'no'
ssh::password_authentication: 'no'

# Packages communs
common_packages:
  - vim
  - git
  - htop
  - curl
  - wget

# NTP
ntp::servers:
  - 0.pool.ntp.org
  - 1.pool.ntp.org


# === data/os/Debian.yaml ===
---
# Apache sur Debian
apache::package_name: apache2
apache::service_name: apache2
apache::config_dir: /etc/apache2


# === data/os/RedHat.yaml ===
---
# Apache sur RedHat
apache::package_name: httpd
apache::service_name: httpd
apache::config_dir: /etc/httpd


# === data/nodes/web1.example.com.yaml ===
---
# Configuration spécifique pour web1
ssh::port: 2222

apache::vhosts:
  mysite:
    server_name: www.example.com
    docroot: /var/www/mysite
    port: 80


# === manifests/site.pp ===
node 'web1.example.com' {
  include ssh
  include common
  include apache
}


# === modules/ssh/manifests/init.pp ===
class ssh (
  Integer $port,
  String $permit_root_login,
  String $password_authentication,
) {
  package { 'openssh-server':
    ensure => installed,
  }
  
  file { '/etc/ssh/sshd_config':
    ensure  => present,
    content => epp('ssh/sshd_config.epp', {
      'port'                     => $port,
      'permit_root_login'        => $permit_root_login,
      'password_authentication'  => $password_authentication,
    }),
    notify  => Service['sshd'],
  }
  
  service { 'sshd':
    ensure => running,
    enable => true,
  }
}


# === modules/common/manifests/init.pp ===
class common {
  $packages = lookup('common_packages', Array, 'unique', [])
  
  package { $packages:
    ensure => installed,
  }
}


# === modules/apache/manifests/init.pp ===
class apache (
  String $package_name,
  String $service_name,
  Hash $vhosts = {},
) {
  package { $package_name:
    ensure => installed,
  }
  
  service { $service_name:
    ensure => running,
    enable => true,
  }
  
  $vhosts.each |$name, $config| {
    apache::vhost { $name:
      * => $config,
    }
  }
}


[OK] FACTER (FACTS SYSTÈME)

# === QU'EST-CE QUE FACTER? ===

# Facter = Outil qui collecte des informations sur le système
# Facts = Informations collectées (OS, IP, RAM, CPU, etc.)

# Exemples de facts:
# - Nom de l'OS (Ubuntu, CentOS, Windows)
# - Version de l'OS (22.04, 8.5, 10)
# - Adresse IP
# - Nom d'hôte
# - Quantité de RAM
# - Nombre de CPUs
# - Architecture (x86_64, arm64)

# Facter s'exécute AUTOMATIQUEMENT avant chaque run Puppet
# Les facts sont utilisables dans les manifests et templates


# === VOIR TOUS LES FACTS ===

# Lister tous les facts:
facter

# Affiche plein d'infos:
# os => {
#   architecture => "x86_64",
#   family => "Debian",
#   name => "Ubuntu",
#   release => {
#     full => "22.04",
#     major => "22.04"
#   }
# }
# networking => {
#   ip => "192.168.1.100",
#   hostname => "web1",
#   fqdn => "web1.example.com"
# }
# memory => {
#   system => {
#     total => "8.00 GiB",
#     available => "5.23 GiB"
#   }
# }


# === VOIR UN FACT SPÉCIFIQUE ===

# Fact simple:
facter os.name
# Affiche: Ubuntu

facter os.family
# Affiche: Debian

facter networking.ip
# Affiche: 192.168.1.100

# Fact structuré (JSON):
facter --json os
# Affiche:
# {
#   "os": {
#     "architecture": "x86_64",
#     "family": "Debian",
#     "name": "Ubuntu",
#     ...
#   }
# }


# === UTILISER LES FACTS DANS LES MANIFESTS ===

# Syntaxe: $facts['categorie']['sous-categorie']

# Exemple 1: Installer un package selon l'OS
$package_name = $facts['os']['family'] ? {
  'Debian' => 'apache2',
  'RedHat' => 'httpd',
  default  => fail('OS non supporté'),
}

package { $package_name:
  ensure => installed,
}


# Exemple 2: Créer un fichier avec des infos système
file { '/etc/system-info.txt':
  ensure  => present,
  content => @("EOT")
    Système: ${facts['os']['name']} ${facts['os']['release']['full']}
    Hostname: ${facts['networking']['fqdn']}
    IP: ${facts['networking']['ip']}
    RAM: ${facts['memory']['system']['total']}
    CPU: ${facts['processors']['count']} cores
    | EOT
}


# Exemple 3: Config conditionnelle
if $facts['os']['family'] == 'Debian' {
  file { '/etc/apt/sources.list.d/myrepo.list':
    ensure  => present,
    content => 'deb http://myrepo.example.com/ubuntu jammy main',
  }
}


# === FACTS COMMUNS ===

# OS et Distribution
$facts['os']['name']             # Ubuntu, CentOS, Windows, macOS
$facts['os']['family']           # Debian, RedHat, Windows, Darwin
$facts['os']['release']['full']  # 22.04, 8.5, 10

# Réseau
$facts['networking']['fqdn']     # web1.example.com
$facts['networking']['hostname'] # web1
$facts['networking']['ip']       # 192.168.1.100
$facts['networking']['mac']      # 00:0c:29:12:34:56

# Matériel
$facts['processors']['count']    # 4
$facts['processors']['models'][0] # Intel(R) Core(TM) i7-8700K
$facts['memory']['system']['total'] # 8.00 GiB
$facts['memory']['system']['available'] # 5.23 GiB

# Identité
$facts['identity']['user']       # root
$facts['identity']['uid']        # 0

# Disques
$facts['disks']                  # Hash de tous les disques
$facts['mountpoints']            # Hash de tous les points de montage

# Virtualisation
$facts['virtual']                # physical, vmware, virtualbox, docker
$facts['is_virtual']             # true / false


# === FACTS PERSONNALISÉS ===

# Tu peux créer tes propres facts!

# === Méthode 1: Script executable ===

# Créer le dossier:
sudo mkdir -p /etc/puppetlabs/facter/facts.d

# Créer un script:
sudo nano /etc/puppet
labs/facter/facts.d/custom_facts.sh

#!/bin/bash
# custom_facts.sh

echo "application_version=1.2.3"
echo "deployment_date=$(date +%Y-%m-%d)"

# Rendre executable:
sudo chmod +x /etc/puppetlabs/facter/facts.d/custom_facts.sh

# Tester:
facter application_version
# Affiche: 1.2.3


# === Méthode 2: Fichier texte ===

# Créer un fichier .txt:
sudo nano /etc/puppetlabs/facter/facts.d/custom.txt

datacenter=us-east-1
environment=production
app_owner=john.doe@example.com

# Tester:
facter datacenter
# Affiche: us-east-1


# === Méthode 3: Fichier YAML ===

# Créer un fichier .yaml:
sudo nano /etc/puppetlabs/facter/facts.d/custom.yaml

---
datacenter: us-east-1
environment: production
app_owner: john.doe@example.com

# Tester:
facter environment
# Affiche: production


# === Méthode 4: Fichier JSON ===

# Créer un fichier .json:
sudo nano /etc/puppetlabs/facter/facts.d/custom.json

{
  "datacenter": "us-east-1",
  "environment": "production",
  "app_owner": "john.doe@example.com"
}


# === Méthode 5: Custom Ruby fact ===

# Pour des facts complexes avec logique

# Créer le fichier:
# modules/mymodule/lib/facter/custom_fact.rb

Facter.add(:custom_fact) do
  setcode do
    # Code Ruby ici
    if File.exist?('/etc/myapp/version')
      File.read('/etc/myapp/version').strip
    else
      'unknown'
    end
  end
end


# === UTILISER LES FACTS PERSONNALISÉS ===

# Dans un manifest:
if $facts['datacenter'] == 'us-east-1' {
  $ntp_servers = ['us-ntp1.example.com', 'us-ntp2.example.com']
} elsif $facts['datacenter'] == 'eu-west-1' {
  $ntp_servers = ['eu-ntp1.example.com', 'eu-ntp2.example.com']
}


# Dans Hiera (hiera.yaml):
---
version: 5

hierarchy:
  - name: "Per datacenter"
    path: "datacenter/%{facts.datacenter}.yaml"
  
  - name: "Per environment"
    path: "environment/%{facts.environment}.yaml"
  
  - name: "Common"
    path: "common.yaml"


[OK] PUPPET FORGE (MODULES EXTERNES)

# === QU'EST-CE QUE PUPPET FORGE? ===

# Puppet Forge = "App Store" pour Puppet
# = Bibliothèque de modules prêts à l'emploi
# = Gratuit et open-source
# URL: https://forge.puppet.com

# Exemples de modules populaires:
# - puppetlabs-apache: Gestion d'Apache
# - puppetlabs-mysql: Gestion de MySQL
# - puppetlabs-docker: Gestion de Docker
# - puppetlabs-postgresql: Gestion de PostgreSQL
# - puppetlabs-nginx: Gestion de Nginx

# Avantages:
# - Pas besoin de réinventer la roue
# - Modules testés et maintenus
# - Communauté active


# === CHERCHER UN MODULE ===

# Sur le site web:
# https://forge.puppet.com/

# Ou avec la CLI:
puppet module search apache

# Affiche:
# NAME                       DESCRIPTION
# puppetlabs-apache          Installs, configures, and manages Apache
# example42-apache           Apache management
# ...


# === INSTALLER UN MODULE ===

# Syntaxe:
puppet module install AUTHOR-MODULE

# Exemple:
sudo puppet module install puppetlabs-apache

# Affiche:
# Notice: Preparing to install into /etc/puppetlabs/code/environments/production/modules ...
# Notice: Downloading from https://forgeapi.puppet.com ...
# Notice: Installing -- do not interrupt ...
# /etc/puppetlabs/code/environments/production/modules
# └─┬ puppetlabs-apache (v5.5.0)
#   ├── puppetlabs-concat (v7.1.1)
#   └── puppetlabs-stdlib (v8.1.0)

# Le module et ses dépendances sont installés!


# === LISTER LES MODULES INSTALLÉS ===

puppet module list

# Affiche:
# /etc/puppetlabs/code/environments/production/modules
# ├── puppetlabs-apache (v5.5.0)
# ├── puppetlabs-concat (v7.1.1)
# └── puppetlabs-stdlib (v8.1.0)


# === UTILISER UN MODULE ===

# Exemple avec puppetlabs-apache:

# manifests/site.pp
node 'webserver1' {
  # Utiliser la classe principale
  class { 'apache':
    default_vhost => false,  # Pas de vhost par défaut
  }
  
  # Créer un virtual host
  apache::vhost { 'mysite.example.com':
    port    => 80,
    docroot => '/var/www/mysite',
  }
  
  # Activer des modules Apache
  apache::mod { 'rewrite': }
  apache::mod { 'ssl': }
}


# === METTRE À JOUR UN MODULE ===

# Voir les modules obsolètes:
puppet module list --tree

# Mettre à jour un module:
sudo puppet module upgrade puppetlabs-apache

# Affiche:
# Notice: Preparing to upgrade 'puppetlabs-apache' ...
# Notice: Found 'puppetlabs-apache' (v5.5.0) in /etc/puppetlabs/code/environments/production/modules ...
# Notice: Downloading from https://forgeapi.puppet.com ...
# /etc/puppetlabs/code/environments/production/modules
# └── puppetlabs-apache (v5.5.0 -> v5.6.0)


# === DÉSINSTALLER UN MODULE ===

sudo puppet module uninstall puppetlabs-apache

# Affiche:
# Notice: Preparing to uninstall 'puppetlabs-apache' ...
# Removed 'puppetlabs-apache' (v5.5.0) from /etc/puppetlabs/code/environments/production/modules


# === EXEMPLE: MODULE MYSQL ===

# Installer le module:
sudo puppet module install puppetlabs-mysql

# Utiliser dans un manifest:
node 'dbserver1' {
  # Installer MySQL Server
  class { 'mysql::server':
    root_password    => 'super-secret-password',
    override_options => {
      'mysqld' => {
        'max_connections' => '1000',
        'bind-address'    => '0.0.0.0',
      }
    },
  }
  
  # Créer une base de données
  mysql::db { 'myapp':
    user     => 'myapp_user',
    password => 'myapp_password',
    host     => 'localhost',
    grant    => ['SELECT', 'INSERT', 'UPDATE', 'DELETE'],
  }
}


# === EXEMPLE: MODULE DOCKER ===

# Installer le module:
sudo puppet module install puppetlabs-docker

# Utiliser dans un manifest:
node 'dockerhost1' {
  # Installer Docker
  include docker
  
  # Télécharger une image
  docker::image { 'nginx':
    image_tag => 'latest',
  }
  
  # Lancer un conteneur
  docker::run { 'nginx-web':
    image   => 'nginx:latest',
    ports   => ['80:80'],
    volumes => ['/var/www:/usr/share/nginx/html:ro'],
  }
}


# === CRÉER TON PROPRE MODULE ===

# Générer la structure d'un module:
puppet module generate mycompany-mymodule

# Affiche des questions:
# We need to create a metadata.json file for this module. Please answer the following questions:
# What version is this module? [0.1.0]
# Who wrote this module? [mycompany]
# What license does this module use? [Apache-2.0]
# ...

# Crée la structure:
# mymodule/
# ├── manifests/
# │   └── init.pp
# ├── files/
# ├── templates/
# ├── lib/
# ├── spec/
# ├── examples/
# ├── metadata.json
# └── README.md


# === PUBLIER TON MODULE SUR PUPPET FORGE ===

# 1. Créer un compte sur https://forge.puppet.com

# 2. Construire le module:
puppet module build mymodule/

# 3. Uploader sur Forge:
puppet module upload mymodule/pkg/mycompany-mymodule-0.1.0.tar.gz


[OK] PUPPET SERVER (MODE AGENT-SERVER)

# === ARCHITECTURE AGENT-SERVER ===

# Jusqu'ici, on a utilisé Puppet en mode standalone
# Maintenant, on va voir le mode agent-server (production!)

# PUPPET SERVER (Master)
# = Serveur central qui gère toutes les configs
# = Contient tous les manifests, modules, Hiera
# = Les agents se connectent à lui

# PUPPET AGENT
# = Installé sur chaque machine gérée
# = Contacte le Puppet Server toutes les 30 min
# = Applique la config reçue

# Flux:
# 1. Agent contacte Server: "Salut, je suis web1, quelle est ma config?"
# 2. Server génère le catalog pour web1
# 3. Server envoie le catalog à l'agent
# 4. Agent applique le catalog
# 5. Agent envoie un rapport au Server


# === INSTALLER PUPPET SERVER ===

# === Sur Ubuntu/Debian ===

# Étape 1: Ajouter le dépôt (si pas déjà fait)
wget https://apt.puppet.com/puppet7-release-jammy.deb
sudo dpkg -i puppet7-release-jammy.deb
sudo apt update

# Étape 2: Installer Puppet Server
sudo apt install puppetserver -y

# Étape 3: Configurer la mémoire (optionnel)
# Par défaut, Puppet Server utilise 2GB de RAM
# Pour réduire (sur petites VMs):
sudo nano /etc/default/puppetserver

# Change:
JAVA_ARGS="-Xms2g -Xmx2g ..."
# En:
JAVA_ARGS="-Xms512m -Xmx512m ..."

# Étape 4: Démarrer Puppet Server
sudo systemctl start puppetserver
sudo systemctl enable puppetserver

# Vérifier:
sudo systemctl status puppetserver
# Doit afficher: active (running)

# Le Server écoute sur le port 8140:
sudo netstat -tlnp | grep 8140


# === Sur CentOS/RedHat ===

# Étape 1: Ajouter le dépôt
sudo rpm -Uvh https://yum.puppet.com/puppet7-release-el-8.noarch.rpm

# Étape 2: Installer
sudo yum install puppetserver -y

# Étape 3 & 4: Même chose que pour Ubuntu


# === CONFIGURER LE PUPPET SERVER ===

# Fichier de config:
sudo nano /etc/puppetlabs/puppet/puppet.conf

# Ajouter/modifier:
[main]
certname = puppet.example.com  # FQDN du serveur
dns_alt_names = puppet,puppet.example.com  # Noms alternatifs
server = puppet.example.com

[master]
# Environnement par défaut
environment = production

# Timeout pour générer le catalog
environment_timeout = unlimited

# Autoriser les agents à se connecter
autosign = false  # Sécurité: ne pas auto-signer

# Redémarrer après modification:
sudo systemctl restart puppetserver


# === INSTALLER PUPPET AGENT (SUR LES NODES) ===

# Sur chaque machine que tu veux gérer:

# Ubuntu/Debian:
wget https://apt.puppet.com/puppet7-release-jammy.deb
sudo dpkg -i puppet7-release-jammy.deb
sudo apt update
sudo apt install puppet-agent -y

# CentOS/RedHat:
sudo rpm -Uvh https://yum.puppet.com/puppet7-release-el-8.noarch.rpm
sudo yum install puppet-agent -y


# === CONFIGURER L'AGENT ===

# Sur chaque agent:
sudo nano /etc/puppetlabs/puppet/puppet.conf

# Ajouter:
[main]
certname = web1.example.com  # FQDN unique du node
server = puppet.example.com  # FQDN du Puppet Server
environment = production
runinterval = 1800  # 30 minutes


# === CONNECTER L'AGENT AU SERVER (CERTIFICATS SSL) ===

# Puppet utilise SSL pour sécuriser les communications

# === Étape 1: Agent demande un certificat ===

# Sur l'agent:
sudo /opt/puppetlabs/bin/puppet agent --test

# Affiche:
# Info: Creating a new SSL key for web1.example.com
# Info: csr_attributes file loading from /etc/puppetlabs/puppet/csr_attributes.yaml
# Info: Creating a new SSL certificate request for web1.example.com
# Info: Certificate Request fingerprint (SHA256): AB:CD:EF:...
# Exiting; no certificate found and waitforcert is disabled


# === Étape 2: Server signe le certificat ===

# Sur le server, voir les demandes en attente:
sudo /opt/puppetlabs/bin/puppetserver ca list

# Affiche:
# Requested Certificates:
#     web1.example.com       (SHA256)  AB:CD:EF:...

# Signer le certificat:
sudo /opt/puppetlabs/bin/puppetserver ca sign --certname web1.example.com

# Affiche:
# Successfully signed certificate request for web1.example.com


# === Étape 3: Agent télécharge le certificat ===

# Sur l'agent, relancer:
sudo /opt/puppetlabs/bin/puppet agent --test

# Affiche:
# Info: Downloaded certificate for web1.example.com from https://puppet.example.com:8140/puppet-ca/v1
# Info: Using configured environment 'production'
# Info: Retrieving pluginfacts
# Info: Retrieving plugin
# Info: Caching catalog for web1.example.com
# Info: Applying configuration version '1640000000'
# Notice: Applied catalog in 0.05 seconds

# BRAVO! L'agent est connecté et applique la config!


# === AUTO-SIGNING (POUR TESTS SEULEMENT!) ===

# ATTENTION: Pas sécurisé en production!

# Sur le server:
sudo nano /etc/puppetlabs/puppet/puppet.conf

# Ajouter:
[master]
autosign = true

# Ou créer une whitelist:
sudo nano /etc/puppetlabs/puppet/autosign.conf

# Ajouter les patterns:
*.example.com
web*.example.com

# Puis dans puppet.conf:
[master]
autosign = /etc/puppetlabs/puppet/autosign.conf

# Redémarrer:
sudo systemctl restart puppetserver


# === DÉMARRER L'AGENT EN SERVICE ===

# Par défaut, l'agent ne tourne pas en continu
# Pour qu'il se connecte toutes les 30 min automatiquement:

# Sur l'agent:
sudo systemctl start puppet
sudo systemctl enable puppet

# Vérifier:
sudo systemctl status puppet
# Doit afficher: active (running)


# === TESTER LA CONNEXION ===

# Sur l'agent, forcer un run immédiat:
sudo /opt/puppetlabs/bin/puppet agent --test

# Affiche:
# Info: Using configured environment 'production'
# Info: Retrieving pluginfacts
# Info: Retrieving plugin
# Info: Loading facts
# Info: Caching catalog for web1.example.com
# Info: Applying configuration version '1640000000'
# Notice: Applied catalog in 0.32 seconds


[OK] EXEMPLE COMPLET: DÉPLOIEMENT D'UNE APP WEB

# === SCÉNARIO ===

# Tu veux déployer une app web PHP avec:
# - Apache + PHP
# - MySQL
# - Firewall configuré
# - Utilisateur dédié
# - Cron pour backup
# - Monitoring

# === ARCHITECTURE ===

# Puppet Server: puppet.example.com
# Node web: web1.example.com


# === ÉTAPE 1: STRUCTURE DU MODULE ===

# Sur le Puppet Server:
sudo mkdir -p /etc/puppetlabs/code/environments/production/modules/webapp/{manifests,files,templates}


# === ÉTAPE 2: MODULE PRINCIPAL (init.pp) ===

sudo nano /etc/puppetlabs/code/environments/production/modules/webapp/manifests/init.pp

class webapp (
  String $db_password,
  String $app_user = 'webapp',
  String $app_group = 'webapp',
  String $app_dir = '/var/www/webapp',
) {
  # Ordre d'exécution
  include webapp::user
  include webapp::packages
  include webapp::config
  include webapp::database
  include webapp::firewall
  include webapp::backup
  
  Class['webapp::user']
  -> Class['webapp::packages']
  -> Class['webapp::config']
  -> Class['webapp::database']
  -> Class['webapp::firewall']
  -> Class['webapp::backup']
}


# === ÉTAPE 3: CRÉER L'UTILISATEUR ===

sudo nano /etc/puppetlabs/code/environments/production/modules/webapp/manifests/user.pp

class webapp::user {
  $app_user = $webapp::app_user
  $app_group = $webapp::app_group
  $app_dir = $webapp::app_dir
  
  # Créer le groupe
  group { $app_group:
    ensure => present,
    gid    => 5000,
  }
  
  # Créer l'utilisateur
  user { $app_user:
    ensure     => present,
    uid        => 5000,
    gid        => $app_group,
    shell      => '/bin/bash',
    home       => $app_dir,
    managehome => true,
    comment    => 'Web Application User',
    require    => Group[$app_group],
  }
}


# === ÉTAPE 4: INSTALLER LES PACKAGES ===

sudo nano /etc/puppetlabs/code/environments/production/modules/webapp/manifests/packages.pp

class webapp::packages {
  # Packages selon l'OS
  case $facts['os']['family'] {
    'Debian': {
      $apache_package = 'apache2'
      $php_packages = ['php', 'php-mysql', 'php-cli', 'php-curl']
      $mysql_package = 'mysql-server'
    }
    'RedHat': {
      $apache_package = 'httpd'
      $php_packages = ['php', 'php-mysqlnd', 'php-cli', 'php-curl']
      $mysql_package = 'mysql-server'
    }
    default: {
      fail("OS ${facts['os']['family']} non supporté")
    }
  }
  
  # Installer Apache
  package { $apache_package:
    ensure => installed,
  }
  
  # Installer PHP
  package { $php_packages:
    ensure => installed,
  }
  
  # Installer MySQL
  package { $mysql_package:
    ensure => installed,
  }
  
  # Packages utilitaires
  package { ['git', 'curl', 'vim']:
    ensure => installed,
  }
}


# === ÉTAPE 5: CONFIGURER APACHE ===

sudo nano /etc/puppetlabs/code/environments/production/modules/webapp/manifests/config.pp

class webapp::config {
  $app_dir = $webapp::app_dir
  $app_user = $webapp::app_user
  $app_group = $webapp::app_group
  
  # Nom du service selon l'OS
  $apache_service = $facts['os']['family'] ? {
    'Debian' => 'apache2',
    'RedHat' => 'httpd',
  }
  
  # Créer le dossier de l'app
  file { $app_dir:
    ensure => directory,
    owner  => $app_user,
    group  => $app_group,
    mode   => '0755',
  }
  
  # Fichier index.php
  file { "${app_dir}/index.php":
    ensure  => present,
    owner   => $app_user,
    group   => $app_group,
    mode    => '0644',
    content => template('webapp/index.php.erb'),
  }
  
  # VirtualHost Apache
  file { '/etc/apache2/sites-available/webapp.conf':
    ensure  => present,
    content => template('webapp/vhost.conf.erb'),
    notify  => Service[$apache_service],
  }
  
  # Activer le site (Debian)
  if $facts['os']['family'] == 'Debian' {
    exec { 'enable-webapp-site':
      command => '/usr/sbin/a2ensite webapp.conf',
      creates => '/etc/apache2/sites-enabled/webapp.conf',
      notify  => Service[$apache_service],
    }
  }
  
  # Démarrer Apache
  service { $apache_service:
    ensure => running,
    enable => true,
  }
}


# === ÉTAPE 6: TEMPLATES ===

# Template VirtualHost:
sudo nano /etc/puppetlabs/code/environments/production/modules/webapp/templates/vhost.conf.erb

<VirtualHost *:80>
    ServerName <%= @fqdn %>
    DocumentRoot <%= @app_dir %>
    
    <Directory <%= @app_dir %>>
        Options -Indexes +FollowSymLinks
        AllowOverride All
        Require all granted
    </Directory>
    
    ErrorLog ${APACHE_LOG_DIR}/webapp-error.log
    CustomLog ${APACHE_LOG_DIR}/webapp-access.log combined
</VirtualHost>


# Template index.php:
sudo nano /etc/puppetlabs/code/environments/production/modules/webapp/templates/index.php.erb

<?php
// Application Web
// Serveur: <%= @fqdn %>
// Généré par Puppet le <%= Time.now %>

phpinfo();
?>


# === ÉTAPE 7: CONFIGURER MYSQL ===

sudo nano /etc/puppetlabs/code/environments/production/modules/webapp/manifests/database.pp

class webapp::database {
  $db_password = $webapp::db_password
  
  # Démarrer MySQL
  service { 'mysql':
    ensure => running,
    enable => true,
  }
  
  # Créer la base de données
  exec { 'create-webapp-db':
    command => "/usr/bin/mysql -e \"CREATE DATABASE IF NOT EXISTS webapp\"",
    unless  => "/usr/bin/mysql -e \"SHOW DATABASES\" | /bin/grep webapp",
    require => Service['mysql'],
  }
  
  # Créer l'utilisateur MySQL
  exec { 'create-webapp-user':
    command => "/usr/bin/mysql -e \"CREATE USER IF NOT EXISTS 'webapp'@'localhost' IDENTIFIED BY '${db_password}'\"",
    unless  => "/usr/bin/mysql -e \"SELECT User FROM mysql.user\" | /bin/grep webapp",
    require => Exec['create-webapp-db'],
  }
  
  # Donner les permissions
  exec { 'grant-webapp-privileges':
    command => "/usr/bin/mysql -e \"GRANT ALL PRIVILEGES ON webapp.* TO 'webapp'@'localhost'\"",
    require => Exec['create-webapp-user'],
  }
}


# === ÉTAPE 8: CONFIGURER LE FIREWALL ===

sudo nano /etc/puppetlabs/code/environments/production/modules/webapp/manifests/firewall.pp

class webapp::firewall {
  # Installer UFW (Ubuntu) ou firewalld (CentOS)
  case $facts['os']['family'] {
    'Debian': {
      package { 'ufw':
        ensure => installed,
      }
      
      # Autoriser SSH
      exec { 'ufw-allow-ssh':
        command => '/usr/sbin/ufw allow 22/tcp',
        unless  => '/usr/sbin/ufw status | /bin/grep "22/tcp"',
        require => Package['ufw'],
      }
      
      # Autoriser HTTP
      exec { 'ufw-allow-http':
        command => '/usr/sbin/ufw allow 80/tcp',
        unless  => '/usr/sbin/ufw status | /bin/grep "80/tcp"',
        require => Package['ufw'],
      }
      
      # Activer UFW
      exec { 'ufw-enable':
        command => '/usr/sbin/ufw --force enable',
        unless  => '/usr/sbin/ufw status | /bin/grep "Status: active"',
        require => [Exec['ufw-allow-ssh'], Exec['ufw-allow-http']],
      }
    }
    'RedHat': {
      package { 'firewalld':
        ensure => installed,
      }
      
      service { 'firewalld':
        ensure  => running,
        enable  => true,
        require => Package['firewalld'],
      }
      
      exec { 'firewall-allow-http':
        command => '/usr/bin/firewall-cmd --permanent --add-service=http && /usr/bin/firewall-cmd --reload',
        unless  => '/usr/bin/firewall-cmd --list-services | /bin/grep http',
        require => Service['firewalld'],
      }
    }
  }
}


# === ÉTAPE 9: CONFIGURER LE BACKUP ===

sudo nano /etc/puppetlabs/code/environments/production/modules/webapp/manifests/backup.pp

class webapp::backup {
  $app_dir = $webapp::app_dir
  
  # Script de backup
  file { '/usr/local/bin/webapp-backup.sh':
    ensure  => present,
    mode    => '0755',
    content => @("SCRIPT")
      #!/bin/bash
      # Backup script généré par Puppet
      
      BACKUP_DIR="/var/backups/webapp"
      DATE=\$(date +%Y%m%d-%H%M%S)
      
      mkdir -p \$BACKUP_DIR
      
      # Backup des fichiers
      tar -czf \$BACKUP_DIR/webapp-files-\$DATE.tar.gz #{$app_dir}
      
      # Backup de la base de données
      mysqldump webapp > \$BACKUP_DIR/webapp-db-\$DATE.sql
      
      # Garder seulement les 7 derniers backups
      find \$BACKUP_DIR -type f -mtime +7 -delete
      | SCRIPT
  }
  
  # Tâche cron (tous les jours à 2h)
  cron { 'webapp-backup':
    command => '/usr/local/bin/webapp-backup.sh',
    user    => 'root',
    hour    => '2',
    minute  => '0',
  }
}


# === ÉTAPE 10: HIERA (DONNÉES) ===

# data/common.yaml
sudo nano /etc/puppetlabs/code/environments/production/data/common.yaml

---
# Mot de passe MySQL (à changer!)
webapp::db_password: 'changeme123'


# data/nodes/web1.example.com.yaml
sudo nano /etc/puppetlabs/code/environments/production/data/nodes/web1.example.com.yaml

---
# Configuration spécifique pour web1
webapp::app_dir: /var/www/myapp
webapp::db_password: 'super-secret-password-web1'


# === ÉTAPE 11: SITE.PP ===

sudo nano /etc/puppetlabs/code/environments/production/manifests/site.pp

# Configuration par défaut
node default {
  notify { 'default-node':
    message => 'Ce node n\'a pas de configuration spécifique',
  }
}

# Configuration pour web1
node 'web1.example.com' {
  include webapp
}


# === ÉTAPE 12: APPLIQUER LA CONFIGURATION ===

# Sur le Puppet Server, vérifier la syntaxe:
sudo puppet parser validate /etc/puppetlabs/code/environments/production/manifests/site.pp
sudo puppet parser validate /etc/puppetlabs/code/environments/production/modules/webapp/manifests/*.pp

# Sur l'agent (web1), appliquer:
sudo /opt/puppetlabs/bin/puppet agent --test

# Affiche plein de logs:
# Info: Using configured environment 'production'
# Info: Retrieving pluginfacts
# Info: Retrieving plugin
# Info: Loading facts
# Info: Caching catalog for web1.example.com
# Info: Applying configuration version '1640000000'
# Notice: /Stage[main]/Webapp::User/Group[webapp]/ensure: created
# Notice: /Stage[main]/Webapp::User/User[webapp]/ensure: created
# Notice: /Stage[main]/Webapp::Packages/Package[apache2]/ensure: created
# Notice: /Stage[main]/Webapp::Packages/Package[php]/ensure: created
# ...
# Notice: Applied catalog in 45.23 seconds

# Vérifier que l'app fonctionne:
# Ouvre un navigateur: http://web1.example.com
# Tu vois la page phpinfo!

# BRAVO! Ton app est déployée!


[OK] DÉBOGAGE & DÉPANNAGE

# === VÉRIFIER LA SYNTAXE ===

# Vérifier un manifest:
puppet parser validate /path/to/manifest.pp

# Si erreur:
# Error: Could not parse for environment production: Syntax error at '=>' ...


# === EXÉCUTER EN MODE DEBUG ===

# Agent en mode verbose:
sudo puppet agent --test --debug

# Affiche BEAUCOUP de détails


# === EXÉCUTER EN MODE NOOP (DRY-RUN) ===

# Voir ce qui serait changé SANS appliquer:
sudo puppet agent --test --noop

# Affiche:
# Notice: /Stage[main]/Main/File[/tmp/test.txt]/ensure: current_value absent, should be present (noop)
# Notice: Class[Main]: Would have triggered 'refresh' from 1 events
# Notice: Applied catalog in 0.05 seconds


# === VOIR LES LOGS ===

# Sur l'agent:
sudo tail -f /var/log/puppetlabs/puppet/puppet.log

# Sur le server:
sudo tail -f /var/log/puppetlabs/puppetserver/puppetserver.log


# === PROBLÈME: Agent ne se connecte pas au Server ===

# Vérifier la résolution DNS:
ping puppet.example.com

# Vérifier que le port 8140 est ouvert:
telnet puppet.example.com 8140

# Vérifier les certificats:
sudo /opt/puppetlabs/bin/puppet agent --test
# Si erreur SSL, supprimer et recréer les certificats:
sudo rm -rf /etc/puppetlabs/puppet/ssl
sudo /opt/puppetlabs/bin/puppet agent --test


# === PROBLÈME: Catalog failed to compile ===

# Vérifier les logs du server:
sudo tail -f /var/log/puppetlabs/puppetserver/puppetserver.log

# Vérifier la syntaxe des manifests:
sudo puppet parser validate /etc/puppetlabs/code/environments/production/manifests/site.pp


# === PROBLÈME: Resource failed to apply ===

# Exemple:
# Error: /Stage[main]/Main/Package[nonexistent]/ensure: change from 'purged' to 'present' failed

# Solution:
# - Vérifier que le package existe
# - Vérifier l'orthographe
# - Vérifier les dépôts (apt update, yum update)


# === LISTER LES CERTIFICATS ===

# Sur le server:
sudo /opt/puppetlabs/bin/puppetserver ca list --all

# Affiche:
# Signed Certificates:
#     web1.example.com    (SHA256)  12:34:56...
#     web2.example.com    (SHA256)  AB:CD:EF...
# Requested Certificates:
#     web3.example.com    (SHA256)  98:76:54...


# === RÉVOQUER UN CERTIFICAT ===

# Sur le server:
sudo /opt/puppetlabs/bin/puppetserver ca revoke --certname web3.example.com
sudo /opt/puppetlabs/bin/puppetserver ca clean --certname web3.example.com


[OK] BONNES PRATIQUES

# === ORGANISATION DU CODE ===

# 1. Un module = Un composant
# Exemple: module apache, module mysql, module users

# 2. Séparer le code en sous-classes
# init.pp = Classe principale
# install.pp = Installation
# config.pp = Configuration
# service.pp = Service

# 3. Utiliser Hiera pour les données
# Ne JAMAIS mettre de mots de passe dans les manifests!

# 4. Versionner avec Git
cd /etc/puppetlabs/code/environments/production
git init
git add .
git commit -m "Initial commit"


# === SÉCURITÉ ===

# 1. Ne pas utiliser autosign en production
# Toujours signer manuellement les certificats

# 2. Utiliser des mots de passe forts
# Stocker dans Hiera avec des permissions restrictives

# 3. Limiter les permissions
# Les manifests doivent être en lecture seule pour les agents

# 4. Auditer les changements
# Activer les rapports Puppet pour suivre qui change quoi


# === PERFORMANCE ===

# 1. Utiliser des modules du Forge quand possible
# Ne pas réinventer la roue!

# 2. Éviter les exec quand possible
# Préférer les resources natives (package, file, service)

# 3. Utiliser environment_timeout
# Cacher le code pour éviter de recompiler à chaque run

# 4. Limiter la fréquence des runs
# 30 minutes est suffisant pour la plupart des cas


# === TESTS ===

# 1. Tester en mode noop avant d'appliquer
sudo puppet agent --test --noop

# 2. Utiliser un environnement de dev
# Ne jamais tester directement en production!

# 3. Valider la syntaxe avant de commit
puppet parser validate manifests/*.pp


[OK] RESSOURCES & LIENS

# Documentation officielle Puppet:
# https://puppet.com/docs/

# Guides spécifiques:
# Getting Started: https://puppet.com/docs/puppet/latest/quick_start.html
# Language Reference: https://puppet.com/docs/puppet/latest/lang_summary.html
# Type Reference: https://puppet.com/docs/puppet/latest/type.html

# Puppet Forge (modules):
# https://forge.puppet.com/

# Communauté:
# Slack: https://puppet-community.slack.com
# Forum: https://ask.puppet.com/
# Reddit: https://www.reddit.com/r/puppet/

# Tutoriels:
# Learn Puppet: https://puppet.com/docs/puppet/latest/quick_start.html
# Puppet by Example: https://github.com/puppet-training/education

# Certification:
# Puppet Certified Professional: https://puppet.com/learning-training/certification/

# Livres recommandés:
# - "Puppet 7 Quick Start Guide" par John Arundel
# - "Learning Puppet 4" par Jo Rhett

# Alternatives à Puppet:
# - Ansible (plus simple, agentless)
# - Chef (similaire à Puppet)
# - SaltStack (très rapide)
# - Terraform (infrastructure as code pour le cloud)


[OK] COMMANDES ESSENTIELLES (RÉFÉRENCE RAPIDE)

# === PUPPET AGENT ===
puppet agent --test                    # Forcer un run immédiat
puppet agent --test --noop             # Dry-run (voir sans appliquer)
puppet agent --test --debug            # Mode debug
puppet agent --test --environment=dev  # Utiliser un environnement spécifique
puppet config print server             # Voir la config

# === PUPPET APPLY (STANDALONE) ===
puppet apply manifest.pp               # Appliquer un manifest
puppet apply --noop manifest.pp        # Dry-run
puppet apply --debug manifest.pp       # Mode debug

# === PUPPET SERVER ===
puppetserver ca list                   # Lister les demandes de certificats
puppetserver ca sign --certname HOST   # Signer un certificat
puppetserver ca revoke --certname HOST # Révoquer un certificat
puppetserver ca clean --certname HOST  # Supprimer un certificat

# === PUPPET MODULE ===
puppet module search KEYWORD           # Chercher un module
puppet module install AUTHOR-MODULE    # Installer un module
puppet module list                     # Lister les modules installés
puppet module upgrade AUTHOR-MODULE    # Mettre à jour un module
puppet module uninstall AUTHOR-MODULE  # Désinstaller un module

# === PUPPET PARSER ===
puppet parser validate FILE.pp         # Valider la syntaxe

# === FACTER ===
facter                                 # Lister tous les facts
facter os.name                         # Voir un fact spécifique
facter --json                          # Output en JSON

# === HIERA ===
puppet lookup KEY                      # Chercher une valeur dans Hiera

# === SERVICES ===
systemctl start puppetserver           # Démarrer Puppet Server
systemctl stop puppetserver            # Arrêter Puppet Server
systemctl restart puppetserver         # Redémarrer Puppet Server
systemctl status puppetserver          # Voir le statut

systemctl start puppet                 # Démarrer Puppet Agent
systemctl stop puppet                  # Arrêter Puppet Agent
systemctl enable puppet                # Activer au démarrage


[OK] FIN DU GUIDE

# FÉLICITATIONS! [BRAVO]
# Tu as maintenant toutes les connaissances pour utiliser Puppet!

# Prochaines étapes recommandées:
# 1. Installer Puppet sur une VM de test
# 2. Créer ton premier module simple
# 3. Explorer le Puppet Forge
# 4. Mettre en place un Puppet Server
# 5. Automatiser la gestion de tes serveurs!

# N'oublie pas:
# - Puppet est puissant mais nécessite de la pratique
# - Commence simple et augmente la complexité progressivement
# - Utilise la communauté (forums, Slack) si tu es bloqué
# - Teste toujours en environnement de dev avant la prod!

# Bonne automatisation! [RAPIDE]
```