PyInfra 🐍

Votre infrastructure mérite du vrai code Python, pas de la soupe YAML 🐍

Fork it! Rouen 2026

Loïc "wowi42" Tosser

🐙 github.com/wowi42
🌲 codeberg.org/wowi42
🦣 @wowi42

Partie I : L'Enfer du YAML 🔥

L'Infrastructure as Code est devenue l'Infrastructure as YAML

Et personne n'avait demandé ça

Le 10ème cercle de Dante, c'était juste un fichier YAML avec une mauvaise indentation

Vous vous souvenez de cette promesse ? 📜

Infrastructure as Code → Infrastructure as YAML

On nous a menti

  • Code : "Un langage de programmation lisible par l'humain"
  • YAML : "Un fichier de définition lisible par la machine" (à peine)

L'un est du code, l'autre est un appel à l'aide.

L'état actuel du DevOps

"Simplifions la gestion de l'infrastructure !"

Et voilà ce qu'on a créé :

- name: Deploy application
  hosts: all
  vars:
    deploy_state: "{{ lookup('env', 'STATE') |
                      default('present') }}"
  tasks:
    - name: Ensure application is {{ deploy_state }}
      when: ansible_os_family == "Debian" and
            not (ansible_distribution == "Ubuntu" and
            ansible_distribution_version is
            version('20.04', '>='))

Sur-simplifié, et pourtant sur-compliqué 😭

Voilà ce qui arrive quand on demande à un fichier de config d'être un langage de programmation

Le cauchemar du développeur

  • Sensibilité à l'indentation → 2 espaces ? 4 ? Des tabulations ? Déploiement annulé.
  • L'enfer du templating → Du Jinja2 dans du YAML dans du Jinja2 ({{ "{{" }} inception {{ "}}" }})
  • Pas de vraies fonctions → Copier-coller des "roles" partout
  • Du debug ? → "ERROR: undefined variable" quelque part dans 47 fichiers
  • Support IDE ? → Du feeling et des prières
  • Le problème de la Norvège → country: NO devient country: false

Les messages d'erreur Ansible 🔍

TASK [Deploy app] *******************
fatal: [web01]: FAILED! => {"msg": "The task includes
an option with an undefined variable. The error was:
'dict object' has no attribute 'nonexistent'\n\nThe
error appears to be in '/playbook.yml': line 42,
column 3, but may be elsewhere in the file depending
on the exact syntax problem."}

"quelque part dans le fichier" — Merci, super utile !

Ajouter -vvvv et scroller 1000 lignes d'output ?

Les tests Ansible 🧪

# 200 lignes de YAML pour tester 50 lignes de YAML, avec Tox et Molecule
# Plus Docker, plus Vagrant, plus un doctorat en souffrance

Yo mec, j'ai entendu que t'aimais le YAML alors j'ai mis du YAML dans tes tests YAML

# Pendant ce temps, en Python :
import pytest  # C'est tout.

Pas de pdb. Pas de breakpoint(). Juste... debug: msg="{{ variable }}".

Partie II : PyInfra entre en scène 🚀

Votre infrastructure. Du vrai Python.

Tout est en Python (y compris votre santé mentale)

>>> import antigravity  # Enfin échapper à l'attraction du YAML
>>> from yaml import pain
ImportError: No more. We're done here.
>>> import pyinfra
>>> # *chef's kiss*

C'est quoi PyInfra ?

Du code Python en entrée, des commandes shell en sortie

pyinfra transforme du code Python en commandes shell et les exécute sur vos serveurs.

  • Exécuter des commandes ad-hoc
  • Écrire des opérations déclaratives
  • Cibler des serveurs SSH, la machine locale, des conteneurs Docker
  • Passe à l'échelle d'un à des milliers de serveurs

Pas besoin de Python sur les systèmes cibles — juste un accès shell POSIX !

Comment fonctionne PyInfra : exécution en deux phases

Phase 1 : Collecte des faits

PyInfra se connecte aux hôtes et collecte des faits sur l'état actuel :

  • Paquets installés, services en cours, checksums des fichiers
  • Détails de l'OS, configuration réseau, utilisateurs

Phase 2 : Exécution des opérations

PyInfra compare l'état souhaité avec les faits actuels :

  • Génère uniquement les commandes shell nécessaires
  • Ignore les opérations dont l'état correspond déjà
  • Exécute les commandes en parallèle sur les hôtes

Résultat : Changements minimaux, efficacité maximale, vraie idempotence

Le concept de base

# Installer et configurer nginx
from pyinfra.operations import apt, systemd

apt.packages(
    name="Install nginx",
    packages=["nginx"],
)

systemd.service(
    name="Restart and enable the nginx service",
    service="nginx.service",
    running=True,
    restarted=True,
    enabled=True,
)

Du Python en entrée, du shell en sortie.

Attendez... C'est juste du Python !

EXACTEMENT ! 🎯

  • De vraies def functions() qui marchent vraiment
  • De vraies for loops sans gymnastique with_items
  • De vrais if/else sans spaghetti Jinja2
  • De vrais import et pytest
  • Type hints & autocomplétion → mypy approuve 👍
  • List comprehensions → [server for server in fleet if server.is_alive()]

Opérations déclaratives 🔄

Ne faire des changements que quand c'est nécessaire

PyInfra intègre la vérification de diff et des opérations idempotentes

# Ne s'exécute que si le paquet n'est pas installé
apt.packages(packages=["nginx"])

# Ne s'exécute que si le service ne tourne pas
systemd.service(service="nginx", running=True)

# Ne s'exécute que si le contenu du fichier diffère
files.put(src="config.j2", dest="/etc/nginx/nginx.conf")

De l'infrastructure déclarative avec du vrai code

Architecture sans agent 💻

Zéro dépendance sur les systèmes cibles

  • Déployez sur n'importe quel système avec un accès shell POSIX
  • Pas d'agents à installer, pas de démons à gérer
  • Pas de bootstrapping nécessaire

Juste SSH et un shell.

Fonctionne avec : Linux, macOS, BSD, conteneurs, VMs cloud, bare metal, routeurs

Connecteurs : ciblez n'importe quoi 🎯

# SSH vers des serveurs (le classique)
pyinfra @ssh/web1.example.com deploy.py

# Exécuter en local (tester avant de casser)
pyinfra @local deploy.py

# Cibler des conteneurs Docker directement
pyinfra @docker/my-container deploy.py

# Mixer dans l'inventaire
pyinfra inventory.py deploy.py
# inventory.py — C'est juste du Python !
ssh_hosts = ["web1", "web2", "db1"]
docker_hosts = [("@docker/app", {"env": "staging"})]
local = ["@local"]

Un outil. Toutes les cibles.

Totalement extensible 📦

Profitez de tout l'écosystème de packages Python

import requests
hosts = requests.get("https://api/hosts").json()

import boto3
instances = boto3.client('ec2').describe_instances()

from slack_sdk import WebClient
slack = WebClient(token=TOKEN)
slack.chat_postMessage(channel="#ops", text="Deploy complete!")

Pas de plugins custom. Juste pip install. Plus de 400 000 packages sur PyPI.

Intégrez PyInfra partout 🧩

C'est une bibliothèque, pas juste un CLI

from pyinfra.api import Config, Inventory, State
from pyinfra.api.operations import run_ops
from pyinfra.operations import server

inventory = Inventory((["web1.example.com"], {}))
state = State(inventory, Config())

server.shell(commands=["echo 'Deployed from my Flask app!'"])
run_ops(state)

De l'automatisation d'infrastructure dans VOTRE appli

Intégration transparente 🔌

  • Récupérez l'inventaire depuis Terraform, Coolify, ou n'importe quelle API
  • Exécutez sur des conteneurs Docker
  • Intégrez dans Django, FastAPI, des outils CLI
import requests
hosts = requests.get("https://api/hosts").json()

from datetime import datetime
if datetime.now().hour > 20:
    print("Pas de déploiement après 20h !")
    exit(1)

Partie III : PyInfra vs Ansible

En quoi c'est mieux ?

Comptons les avantages...

Performance : jusqu'à 10x plus rapide 🚀

Ansible :

  • Séquentiel par défaut
  • Couches d'abstraction lourdes
  • Python requis sur la cible

PyInfra :

  • Multiplexage SSH efficace
  • Exécution parallèle intégrée
  • Commandes shell directes
  • Passe à l'échelle sur des milliers de serveurs

Jusqu'à 10x plus rapide en déploiement

Face à face : installation de paquets

Ansible (YAML verbeux)

- name: Install packages
  hosts: webservers
  become: yes
  tasks:
    - name: Update apt cache
      apt:
        update_cache: yes

    - name: Install packages
      apt:
        name: "{{ item }}"
        state: present
      with_items:
        - nginx
        - postgresql
        - redis

18 lignes pour installer 3 paquets

Face à face : installation de paquets

PyInfra (du Python propre)

from pyinfra.operations import apt

apt.packages(
    packages=["nginx", "postgresql", "redis"],
    update=True,
)

6 lignes contre 18

Face à face : logique conditionnelle

Ansible

- name: Configure based on environment
  template:
    src: "{{ 'prod.j2' if env == 'production'
             else 'dev.j2' }}"
    dest: /etc/app/config
  when: env is defined

PyInfra

template = "prod.j2" if env == "production" else "dev.j2"
files.template(src=template, dest="/etc/app/config")

Juste du Python. Pas de gymnastique Jinja2.

Face à face : les boucles

Ansible

- name: Create users
  user:
    name: "{{ item.name }}"
    groups: "{{ item.groups }}"
  with_items:
    - { name: alice, groups: sudo }
    - { name: bob, groups: developers }
  when: item.name != 'root'

Face à face : les boucles

PyInfra

users = [
    {"name": "alice", "groups": ["sudo"]},
    {"name": "bob", "groups": ["developers"]}
]

for user in users:
    if user["name"] != "root":
        server.user(user.name, groups=user.groups)

Les list comprehensions > with_items

Debug : retour instantané 🚨

Ansible

  • Ajouter le flag -vvvv et scroller des milliers de lignes
  • Utiliser des tâches debug
  • Bonne chance pour trouver la vraie erreur

PyInfra

  • Output des commandes shell en temps réel
  • Utiliser pdb (breakpoint() fonctionne directement)
  • Support du debugger IDE avec des breakpoints

Messages d'erreur : de vraies stack traces

Traceback (most recent call last):
  File "deploy.py", line 42, in <module>
    config["nonexistent"]
KeyError: 'nonexistent'

Fichier exact, ligne, et erreur. Fini le "may be elsewhere".

Tests : de vrais tests unitaires 🧪

PyInfra

import pytest
from deploy import setup_nginx

def test_nginx_config():
    config = setup_nginx("production")
    assert "ssl" in config
    assert config["worker_processes"] == 4

C'est du Python. Testez-le comme du Python.

Support IDE : développement moderne

Ansible

  • Coloration syntaxique YAML basique
  • Peut-être un peu de linting
  • Pas d'autocomplétion, pas de refactoring

PyInfra

  • Autocomplétion complète avec type hints
  • Documentation inline
  • Support du refactoring (renommer, extraire)
  • Aller à la définition

Réutilisation de code : modules Python

Ansible

  • Copier-coller des rôles depuis Galaxy
  • Personnaliser avec 5 répertoires différents
  • Se battre avec les dépendances de rôles

PyInfra

  • import vos fonctions
  • Utilisez plus de 400 000 packages PyPI
  • Écrivez du code propre et modulaire
from my_company.infra import setup_monitoring, configure_firewall

Bénéfices en production 📈

  • Jusqu'à 10x plus rapide qu'Ansible
  • 70% de code en moins à maintenir
  • Vraie réutilisation de code via import
  • 5x plus rapide en développement avec le support IDE
  • De vrais tests unitaires avec pytest
  • Les juniors comprennent — ils ont appris Python
  • Passe à l'échelle sur des milliers de serveurs
  • Intégrable dans vos applis

Mais et si... 🤔

"Ansible a des milliers de modules !"

PyInfra a plus de 400 000 packages Python. pip install requests > module Ansible custom.

"Tout le monde connaît Ansible !"

Tout le monde connaît mieux Python. Et déteste débuguer Ansible.

"Le déclaratif c'est mieux !"

PyInfra EST déclaratif. Vérification de diff, opérations idempotentes. Avec du vrai code.

Mais et si... (suite)

"Et les dépendances sur les systèmes cibles ?"

Zéro. Sans agent. N'importe quel système avec un accès shell POSIX.

"Mais j'ai besoin de l'intégration Terraform/Docker !"

PyInfra fait les deux. Question suivante ?

"Le changement fait peur !"

Votre playbook de 5000 lignes que personne n'ose toucher aussi.

Chemin de migration 🛤️

  1. Commencez petit — Un playbook à la fois
  2. Utilisez votre IDE — Autocomplétion, refactoring
  3. Profitez de Python — packages pip, stdlib
  4. Testez en local — pyinfra @local deploy.py
  5. Dormez mieux — Fini le debug à 3h du mat'

De nombreuses équipes ont migré avec succès d'Ansible vers pyinfra.

Prêt pour la production ?

Prêt pour la production 🌟

  • Éprouvé au combat — Des entreprises dans le monde entier gèrent leur prod avec
  • APIs stables — Pas de casse entre les versions
  • Couverture de tests complète
  • Maintenance active — Releases régulières
  • Licence MIT — Open source et gratuit pour toujours

Cas d'usage

🖥️ Provisionnement de serveurs — Du bare metal au prêt-pour-la-prod
🔄 Déploiement d'applications — Rolling updates avec health checks
⚙️ Gestion de configuration — Gardez votre infrastructure synchronisée
🔧 Automatisation ad-hoc — Des commandes sur des centaines de serveurs
🐳 Orchestration de conteneurs — Gérez vos conteneurs Docker
☁️ Infrastructure cloud — Terraform et fournisseurs cloud

Si ça a SSH, PyInfra peut lui parler.

Lancez-vous aujourd'hui ! 🚀

pip install pyinfra  # La partie difficile. C'est fait.
# deploy.py
from pyinfra.operations import apt, systemd

apt.packages(name="Install nginx", packages=["nginx"])

systemd.service(
    name="Start nginx",
    service="nginx.service",
    running=True,
    enabled=True,
)
pyinfra @localhost deploy.py  # *chef's kiss*

C'est tout. De l'IaC avec du vrai code.

Arrêtez le YAML, passez au Python

Venez me voir après pour discuter de :

  • Vos histoires d'horreur Ansible (thérapie de groupe 🛋️)
  • Stratégies de migration
  • Pourquoi le YAML vous a fait du mal
  • Comment import pyinfra peut vous guérir

Contribuez 🔗

resources = {
    "docs": "docs.pyinfra.com",
    "github": "github.com/pyinfra-dev/pyinfra",
    "pypi": "pip install pyinfra",
    "examples": "github.com/pyinfra-dev/pyinfra/tree/3.x/examples",
}

Starrez le repo ⭐ • Essayez aujourd'hui • Ne touchez plus jamais au YAML

Merci ! 🙏

return "Allez libérer votre infrastructure !"

🐍 pyinfra.com → Votre futur sans YAML commence maintenant

if encore_du_ansible_apres_ce_talk:
    raise FriendshipError("Faut qu'on parle")

On se retrouve au bar après ? 🍻🧀

SCRIPT: "PyInfra n'est pas qu'un CLI — c'est une bibliothèque. Intégrez-la dans Django, FastAPI, n'importe où."

SCRIPT: "C'est juste du Python. Récupérez l'inventaire depuis Terraform, ajoutez des freezes de déploiement, bloquez les déploiements du vendredi — tout en Python standard."

SCRIPT: "PyInfra versus Ansible. Comptons les avantages..."

SCRIPT: "Jusqu'à 10x plus rapide. Multiplexage SSH, exécution parallèle, commandes shell directes."

SCRIPT: "18 lignes de YAML pour installer 3 paquets."

SCRIPT: "PyInfra : 6 lignes. Un import, un appel de fonction, terminé."

SCRIPT: "Logique conditionnelle : du Jinja2 dans du YAML vs une expression ternaire."

SCRIPT: "Les boucles. En Ansible : with_items, with_dict, with_file, with_nested... En Python : une boucle for."

SCRIPT: "En PyInfra : une boucle for. Celle que vous avez apprise la première semaine de Python."

SCRIPT: "Debug : -vvvv et scroller des milliers de lignes, ou utiliser pdb comme une personne civilisée."

SCRIPT: "PyInfra : fichier, numéro de ligne, problème exact. KeyError à la ligne 42. Terminé."

SCRIPT: "Tests : import pytest, écrire des tests. Pas de molecule, pas de test kitchen."

SCRIPT: "Support IDE : coloration YAML vs autocomplétion complète, refactoring, aller à la définition."

SCRIPT: "Réutilisation de code : copier des rôles depuis Galaxy ou juste importer un module."

SCRIPT: "10x plus rapide. 70% de code en moins. Les juniors comprennent parce qu'ils ont appris Python."

SCRIPT: "Objections : Ansible a des modules ? PyInfra a 400k packages. Tout le monde connaît Ansible ? Tout le monde déteste le debug."

SCRIPT: "Zéro dépendance sur les cibles. Intégration Terraform et Docker. Le changement fait peur — le playbook de 5000 lignes aussi."

SCRIPT: "Commencez petit. Prenez un playbook. Convertissez-le. Savourez."

SCRIPT: "Prêt pour la production. APIs stables. Maintenance active. Licence MIT."

SCRIPT: "Prêt pour la production. APIs stables. Maintenance active. Licence MIT."

SCRIPT: "Cas d'usage : provisionnement de serveurs, déploiement, configuration, conteneurs. Si ça a SSH, PyInfra peut lui parler."

SCRIPT: "pip install pyinfra. Écrivez deploy.py. Lancez. Terminé. Essayez pendant les questions."

SCRIPT: "Arrêtez le YAML. Passez au Python. Venez me voir après pour une thérapie de groupe."

SCRIPT: "Starrez le repo. Essayez aujourd'hui. Ne touchez plus jamais au YAML."

SCRIPT: "Merci ! Allez libérer votre infrastructure ! On se retrouve au bar après ? Des questions ?"