
Vous avez une config à gérer côté managed node — choisissez-vous lineinfile: (modifie une ligne précise) ou template: (génère le fichier complet) ? Ce choix est moins évident qu’il n’y paraît : lineinfile est tentant parce que rapide à écrire, mais il devient un anti-pattern dès qu’on dépasse 2-3 lignes. À l’inverse, template est puissant mais demande de posséder le fichier complet (vous le générez de zéro). Cette page donne les critères de choix précis et désamorce les pièges classiques.
Ce que vous allez apprendre
Section intitulée « Ce que vous allez apprendre »- Les 2 cas d’usage clairs de
lineinfile:(et seulement ceux-là) - Les 2 cas d’usage clairs de
template: - L’usage de
blockinfile:(bloc multi-lignes ponctuel) - Les anti-patterns :
lineinfile× 30,templatequi écrase un fichier que vous ne possédez pas - Critères de décision sur 3 questions
Prérequis
Section intitulée « Prérequis »- Avoir lu Module template ;
- Avoir manipulé
lineinfilesur le labbootstrap/prepare-managed-nodes(cf. la tâche/etc/hosts).
Tableau de décision rapide
Section intitulée « Tableau de décision rapide »| Vous voulez… | Module |
|---|---|
Ajouter / modifier 1 ligne précise dans un fichier que vous ne possédez pas (/etc/sysctl.conf, /etc/sudoers) | lineinfile: |
| Insérer un bloc de N lignes (instructions cloud-init, snippet) | blockinfile: |
Générer un fichier de config complet (nginx.conf, myapp.conf, sshd_config) | template: |
| Effacer + régénérer un fichier à chaque run | template: ou copy: |
| Modifier 30 lignes d’un fichier de config | template: (jamais 30 lineinfile:) |
lineinfile — quand c’est le bon choix
Section intitulée « lineinfile — quand c’est le bon choix »# ✅ Activer IP forwarding (1 ligne)- ansible.builtin.lineinfile: path: /etc/sysctl.conf regexp: '^net.ipv4.ip_forward' line: 'net.ipv4.ip_forward = 1'
# ✅ Inscrire un host dans /etc/hosts- ansible.builtin.lineinfile: path: /etc/hosts regexp: '^192\.168\.99\.99\s' line: '192.168.99.99 mon-host.lab'
# ✅ Ajouter un alias dans /etc/sudoers (avec validate)- ansible.builtin.lineinfile: path: /etc/sudoers.d/admin line: 'admin ALL=(ALL) NOPASSWD:ALL' validate: '/usr/sbin/visudo -cf %s' create: truePattern clair : 1 ligne précise, identifiable par une regex, dans un fichier dont vous ne contrôlez pas le reste.
blockinfile — bloc multi-lignes ponctuel
Section intitulée « blockinfile — bloc multi-lignes ponctuel »Pour insérer plusieurs lignes sans posséder tout le fichier :
- ansible.builtin.blockinfile: path: /etc/sysctl.conf block: | net.ipv4.ip_forward = 1 net.ipv6.conf.all.forwarding = 1 net.ipv4.conf.all.rp_filter = 1 marker: "# {mark} ANSIBLE MANAGED forwarding"marker: génère deux marqueurs # BEGIN ANSIBLE MANAGED forwarding et # END ANSIBLE MANAGED forwarding autour du bloc — Ansible les utilise pour le remettre à jour ou le supprimer au prochain run.
À utiliser pour 3-10 lignes consécutives. Au-delà, basculez sur template:.
template — quand c’est le bon choix
Section intitulée « template — quand c’est le bon choix »# ✅ Générer un fichier de config applicatif complet- ansible.builtin.template: src: templates/myapp.conf.j2 dest: /etc/myapp/myapp.conf mode: "0644"
# ✅ Régénérer nginx.conf à chaque run (vous le possédez)- ansible.builtin.template: src: templates/nginx.conf.j2 dest: /etc/nginx/nginx.conf validate: '/usr/sbin/nginx -t -c %s' backup: true notify: Reload nginxPattern clair : vous possédez le fichier complet, le contenu est généré à partir de variables Ansible, l’idempotence est gérée par checksum.
Anti-patterns à éviter
Section intitulée « Anti-patterns à éviter »Anti-pattern 1 — lineinfile: × 30
Section intitulée « Anti-pattern 1 — lineinfile: × 30 »Si vous écrivez plus de 3 lineinfile: sur le même fichier, vous avez un problème de design. Exemples vus en code legacy :
# ❌ ANTI-PATTERN- ansible.builtin.lineinfile: { path: /etc/nginx/nginx.conf, regexp: '^worker_processes', line: 'worker_processes 4;' }- ansible.builtin.lineinfile: { path: /etc/nginx/nginx.conf, regexp: '^worker_connections', line: 'worker_connections 1024;' }- ansible.builtin.lineinfile: { path: /etc/nginx/nginx.conf, regexp: '^server_tokens', line: 'server_tokens off;' }- ansible.builtin.lineinfile: { path: /etc/nginx/nginx.conf, regexp: '^keepalive_timeout', line: 'keepalive_timeout 65;' }# ... 26 lignes plus loin→ Migrer vers template: avec un fichier nginx.conf.j2 complet. Plus lisible, plus rapide à exécuter, plus facile à maintenir.
Anti-pattern 2 — template: sur un fichier que vous ne possédez pas
Section intitulée « Anti-pattern 2 — template: sur un fichier que vous ne possédez pas »# ❌ ANTI-PATTERN- ansible.builtin.template: src: my-sysctl.conf.j2 dest: /etc/sysctl.conf # ← écrase TOUT le contenu existant/etc/sysctl.conf peut contenir des entrées ajoutées par d’autres paquets (/etc/sysctl.d/*.conf aussi). Les écraser casse des choses.
→ Préférer lineinfile: (ou un nouveau fichier dans /etc/sysctl.d/99-myapp.conf que vous possédez).
Anti-pattern 3 — lineinfile: sans regexp:
Section intitulée « Anti-pattern 3 — lineinfile: sans regexp: »# ❌ ANTI-PATTERN- ansible.builtin.lineinfile: path: /etc/hosts line: '192.168.99.99 mon-host.lab' # ← ajoutée à CHAQUE runSans regexp:, Ansible vérifie si la ligne exacte existe. Si vous changez la valeur (192.168.99.99 → 192.168.99.100), l’ancienne ligne reste ET la nouvelle est ajoutée → doublon.
→ Toujours poser un regexp: qui identifie la ligne par son pattern stable :
# ✅ Avec regexp pour idempotence- ansible.builtin.lineinfile: path: /etc/hosts regexp: '^192\.168\.99\.99\s' # match la ligne par IP line: '192.168.99.99 mon-host.lab'Les 3 questions de décision
Section intitulée « Les 3 questions de décision »Quand vous hésitez, demandez-vous :
-
Combien de lignes vous voulez gérer dans ce fichier ?
- 1-2 →
lineinfile: - 3-10 →
blockinfile:(avec marker) - 10+ →
template:
- 1-2 →
-
Vous possédez le fichier complet ?
- Oui (vous générez tout) →
template: - Non (d’autres systèmes y écrivent) →
lineinfile:/blockinfile:
- Oui (vous générez tout) →
-
Le fichier a un format structuré que
lineinfilepeut parser proprement ?- Format simple
key = value→lineinfile:OK - Format complexe avec blocs (
server { ... }, sections[xxx]) →template:
- Format simple
Cas pratique — les 2 approches dans un même playbook (lab ecrire-code/lineinfile-vs-template)
Section intitulée « Cas pratique — les 2 approches dans un même playbook (lab ecrire-code/lineinfile-vs-template) »Voici l’exemple validé sur le lab 30-ecrire-code-lineinfile-vs-template :
- name: Challenge lineinfile vs template hosts: db1.lab become: true vars: server: host: "0.0.0.0" port: 8080 workers: 4 database: url: "postgres://db1.lab/myapp" pool_size: 10 tasks: # 1 ligne : lineinfile (on ne possède pas /etc/hosts complet) - name: Ajouter une entrée DNS via lineinfile ansible.builtin.lineinfile: path: /etc/hosts regexp: '^192\.168\.99\.99\s' line: '192.168.99.99 mon-host.lab'
# Fichier complet : template (on possède /etc/myapp.conf) - name: Générer le fichier de config app via template ansible.builtin.template: src: templates/myapp.conf.j2 dest: /etc/myapp.conf owner: root group: root mode: "0644"/etc/hosts reçoit une seule ligne ajoutée. /etc/myapp.conf est généré complètement depuis le template. Chaque module sur son terrain.
À retenir
Section intitulée « À retenir »lineinfile:= 1-2 lignes ciblées par regex dans un fichier que vous ne possédez pas.blockinfile:= bloc multi-lignes (3-10) avec marker délimitant la zone managée.template:= fichier complet généré depuis un.j2+ variables Ansible.- Anti-pattern n°1 : 30
lineinfile:sur le même fichier → migrer verstemplate:. - Anti-pattern n°2 :
template:qui écrase un fichier partagé (/etc/sysctl.conf) → revenir àlineinfile:ou créer un nouveau fichier dans le.d/. - 3 questions de décision : nombre de lignes, propriété du fichier, complexité du format.
Pratiquer dans le lab
Section intitulée « Pratiquer dans le lab »Cette page a un lab d’accompagnement : labs/ecrire-code/lineinfile-vs-template/ dans
stephrobert/ansible-training. Le challenge
combine les 2 approches dans un même playbook (lineinfile sur /etc/hosts,
template pour /etc/myapp.conf).
cd ~/Projets/ansible-training/labs/ecrire-code/lineinfile-vs-template/cat README.mdpytest -v challenge/tests/