
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.