
Le module ansible.builtin.lineinfile modifie une seule ligne dans un fichier existant : ajouter si absente, remplacer une ligne matchant une regexp, ou supprimer. C'est l'outil de base pour éditer un fichier de configuration dont on contrôle juste quelques paramètres — sshd_config, sudoers, /etc/hosts, /etc/sysctl.conf. Public visé : débutants et intermédiaires Ansible. Cette page couvre les 4 cas d'usage (ajouter, remplacer, supprimer, valider), les pièges classiques (regexp ancrée vs non-ancrée, idempotence cassée), et les alternatives quand lineinfile n'est pas le bon outil (blockinfile, template, replace).
Ce que vous allez apprendre
Section intitulée « Ce que vous allez apprendre »- Ajouter une ligne dans un fichier si elle n'y est pas déjà.
- Remplacer une ligne matchant une regexp par une nouvelle valeur.
- Supprimer une ligne identifiée par regexp.
- Utiliser
validate:pour ne jamais écrire un fichier de config syntaxiquement cassé. - Reconnaître quand
lineinfilen'est pas le bon outil (et préférertemplate,blockinfile,replace).
Prérequis
Section intitulée « Prérequis »- Connaissance de base d'Ansible (cf. Premiers pas).
- Notions de regex POSIX étendues —
lineinfileutilise du regex Python style. - Un nœud cible avec un fichier de config simple à modifier (Linux).
Le cas le plus simple — ajouter une ligne si absente
Section intitulée « Le cas le plus simple — ajouter une ligne si absente »C'est l'usage par défaut quand on ne précise ni regexp ni state :
- name: Activer le forwarding IPv4 dans sysctl ansible.builtin.lineinfile: path: /etc/sysctl.conf line: "net.ipv4.ip_forward=1" state: present create: falseComportement :
- Si la ligne
net.ipv4.ip_forward=1est déjà dans le fichier (matching exact) →ok(rien à faire). - Sinon → la ligne est ajoutée à la fin du fichier.
create: false (défaut) refuse de créer le fichier s'il n'existe pas — task échoue. Mettre create: true pour qu'Ansible crée le fichier s'il manque.
Cas n°2 — remplacer une ligne via regexp
Section intitulée « Cas n°2 — remplacer une ligne via regexp »Le pattern idiomatique pour modifier un paramètre de config :
- name: Désactiver le login root SSH ansible.builtin.lineinfile: path: /etc/ssh/sshd_config regexp: '^\s*#?\s*PermitRootLogin\s' line: "PermitRootLogin no" state: present validate: "sshd -t -f %s" notify: Restart sshdLogique :
regexpidentifie la ligne à remplacer — ici toute ligne qui commence parPermitRootLogin(avec ou sans#de commentaire, espaces optionnels).- Si la regexp matche une ligne existante → cette ligne est remplacée par la valeur de
line. - Si aucune ligne ne matche → la ligne est ajoutée à la fin du fichier.
validate: "sshd -t -f %s"lancesshd -t -f /tmp/<temp>sur le fichier candidat avant de l'écrire en place. Si la validation échoue, le fichier original n'est pas touché.
%s est remplacé par le chemin temporaire qu'Ansible utilise pour le fichier candidat — pas le chemin du fichier final. C'est mandatory pour les configs critiques (sshd, sudoers, nginx, postgres) où une faute de frappe peut tout casser.
Validation par module — le tableau des configs critiques
Section intitulée « Validation par module — le tableau des configs critiques »| Fichier | Validate à utiliser |
|---|---|
/etc/ssh/sshd_config | sshd -t -f %s |
/etc/sudoers ou /etc/sudoers.d/* | visudo -cf %s |
/etc/nginx/nginx.conf ou vhost | nginx -t -c %s |
/etc/httpd/conf/httpd.conf | httpd -t -f %s (Apache RHEL) |
/etc/named.conf | named-checkconf %s |
/etc/postgresql/main/pg_hba.conf | (pas de validateur natif — vérifier après reload) |
/etc/cron.d/* | (pas de validateur — crontab -T côté user) |
Sur tout fichier de config qui peut casser un service, l'omission de validate: est une erreur qui peut sortir le serveur d'accès distant — règle non négociable du pilier Security du WAF.
Cas n°3 — supprimer une ligne
Section intitulée « Cas n°3 — supprimer une ligne »Pour enlever une ligne, mettre state: absent. Le regexp identifie la ligne ; line n'est pas requis.
- name: Retirer une ligne d'allowlist obsolète dans /etc/hosts.allow ansible.builtin.lineinfile: path: /etc/hosts.allow regexp: '^sshd:\s+192\.168\.42\.\d+' state: absentSi plusieurs lignes matchent la regexp, toutes sont supprimées. Si aucune ne matche, la task est ok (idempotent).
Cas n°4 — backrefs pour préserver une partie de la ligne
Section intitulée « Cas n°4 — backrefs pour préserver une partie de la ligne »Quand vous voulez modifier une seule valeur dans une ligne mais conserver le reste tel quel (espaces, commentaires…), utilisez backrefs: true + groupes de capture dans la regexp :
- name: Augmenter MaxAuthTries SSH sans toucher au reste de la ligne ansible.builtin.lineinfile: path: /etc/ssh/sshd_config regexp: '^(\s*MaxAuthTries\s+).*$' line: '\g<1>3' backrefs: true validate: "sshd -t -f %s"Comportement avec backrefs: true :
- Si la regexp matche → ligne remplacée en utilisant les groupes
\g<1>,\g<2>, etc. - Si la regexp ne matche pas → la ligne n'est PAS ajoutée. Différence cruciale par rapport au comportement par défaut.
C'est volontaire : avec backrefs, on suppose que la ligne existe déjà sous une forme reconnaissable. Si elle n'existe pas, c'est suspect — au lieu d'ajouter une ligne par défaut, Ansible préfère ne rien faire.
insertafter et insertbefore — placer la ligne ailleurs qu'à la fin
Section intitulée « insertafter et insertbefore — placer la ligne ailleurs qu'à la fin »Par défaut, une nouvelle ligne (cas où la regexp ne matche rien) est ajoutée à la fin du fichier. Pour la placer ailleurs :
# Ajouter une ligne JUSTE APRÈS [main] dans un .ini- name: Forcer le user dans la section [main] ansible.builtin.lineinfile: path: /etc/something.conf regexp: '^user=' line: "user=outscale" insertafter: '^\[main\]'
# Ajouter une ligne JUSTE AVANT la première directive Listen- name: Préfixer une directive Apache ansible.builtin.lineinfile: path: /etc/httpd/conf/httpd.conf regexp: '^ServerTokens' line: "ServerTokens Prod" insertbefore: '^Listen' validate: "httpd -t -f %s"insertafter et insertbefore prennent une regexp. Valeurs spéciales :
EOF(défaut pourinsertafterquand non précisé) : fin de fichier.BOF: début de fichier.
Quand lineinfile n'est pas le bon outil
Section intitulée « Quand lineinfile n'est pas le bon outil »lineinfile est précieux pour les petites modifications ciblées. Mais c'est rarement le bon choix quand :
| Situation | Outil préférable |
|---|---|
| Vous voulez gérer tout un fichier de config | template + Jinja2 (cf. Templates Jinja2) |
| Vous voulez ajouter/modifier plusieurs lignes liées (un bloc) | blockinfile (cf. Module blockinfile) |
| Vous voulez remplacer un motif dans une ligne (même partielle) | replace (cf. Module replace) |
Vous gérez un fichier .ini ou .yaml | ini_file ou yaml modules dédiés — plus sûrs |
| Vous voulez modifier un fichier JSON | replace + regex, ou mieux : modifier en Python via script ou command |
Règle simple : si vous écrivez 3 tasks lineinfile sur le même fichier, c'est probablement le moment de basculer sur template.
Idempotence — comment vérifier
Section intitulée « Idempotence — comment vérifier »L'idempotence d'un lineinfile se vérifie par un double run :
# 1er run — task = `changed` ou `ok` selon état initialansible-playbook configure-sshd.yml
# 2e run immédiat — task DOIT être `ok` (jamais `changed`)ansible-playbook configure-sshd.ymlSi le 2e run rapporte changed: 1, votre regexp ne matche pas la ligne que vous venez d'écrire. Causes typiques :
- Espaces ou tabulations dans la regexp qui ne matchent pas la ligne réelle.
- Ancres
^et$manquantes — regexp trop lâche. - Caractères spéciaux non échappés (
.,*,[,],\).
Tester la regexp manuellement sur le fichier après le 1er run :
grep -E "<votre-regexp>" /etc/ssh/sshd_config# Doit matcher exactement la ligne que vous venez d'écrirePièges courants
Section intitulée « Pièges courants »| Symptôme | Cause | Solution |
|---|---|---|
| Ligne ajoutée 2 fois (matching exact échoue) | regexp non précisée + différence d'espacement | Toujours préciser regexp pour identifier le paramètre |
changed à chaque run (idempotence cassée) | regexp ne matche pas la ligne écrite par line | Tester la regexp avec grep -E après le 1er run |
backrefs: true mais ligne manquante non ajoutée | Comportement volontaire de backrefs | Si la ligne peut être absente : 2 tâches (1 sans backrefs pour ajouter, 1 avec pour remplacer) |
validate qui échoue mais avec un message obscur | %s non utilisé ou mauvais flag du validateur | Tester d'abord en CLI : sshd -t -f /etc/ssh/sshd_config |
| Service pas redémarré après modification | notify: manquant | Ajouter notify: Restart sshd + un handler correspondant |
Permission denied à l'écriture | become: true manquant | become: true au play ou à la tâche |
Fichier sans \n final après lineinfile | Bug rare avec certains validators | Ajouter lineinfile final qui force une newline si nécessaire |
À retenir
Section intitulée « À retenir »lineinfilemodifie une seule ligne — pour plusieurs, préférerblockinfileoutemplate.regexpidentifie la ligne par son paramètre, pas par son contenu exact (qui peut varier en espaces).validate: "<cmd> -t -f %s"est mandatory sur tout fichier critique (sshd, sudoers, nginx).state: absent+regexp= supprime toutes les lignes matchant.backrefs: true= remplace si la regexp matche, ne fait rien si elle ne matche pas (volontaire).insertafter/insertbeforeplacent la ligne uniquement quand la regexp principale ne matche pas (insertion).- Tester l'idempotence par un double run —
changedau 2e run = regexp incorrecte. - 3 lineinfile sur le même fichier = passez à
template.
Pratiquer dans le lab
Section intitulée « Pratiquer dans le lab »Cette page a un lab d'accompagnement : labs/modules-fichiers/lineinfile/ dans stephrobert/ansible-training.
Challenge — sur db1.lab :
- Ajouter
net.ipv4.ip_forward=1dans/etc/sysctl.conf(ajout simple). - Forcer
PermitRootLogin nodans/etc/ssh/sshd_config(regexp +validate: "sshd -t -f %s"). - Réduire
MaxAuthTries 3en préservant l'indentation existante (backrefs: true). - Ajouter
AllowUsers ansiblesi la directive n'existe pas.
Validation pytest+testinfra :
ansible-playbook labs/modules-fichiers/lineinfile/challenge/solution.ymlansible-playbook labs/modules-fichiers/lineinfile/challenge/solution.yml # 2e run = changed=0pytest -v labs/modules-fichiers/lineinfile/challenge/tests/Les tests vérifient la présence de chaque directive, le succès de sshd -T, et l'idempotence via un double run sans changed.