Aller au contenu
English
English
Infrastructure as Code medium

Module Ansible replace : remplacer un motif dans un fichier existant

70 min de lecture

Logo Ansible

Le module ansible.builtin.replace substitue toutes les occurrences d'un motif regex dans un fichier, sans toucher au reste de la ligne ni du fichier. C'est l'outil pour les changements transversaux : mettre à jour une URL d'API, changer un nom d'hôte qui apparaît à 15 endroits, propager une nouvelle version. Public visé : intermédiaires Ansible. Cette page distingue replace de lineinfile (différence cruciale), montre les patterns d'usage avec before et after pour limiter la zone de remplacement, et liste les pièges classiques (regex gourmande, idempotence cassée, capture group mal échappée).

  • Remplacer un motif regex par une nouvelle valeur dans un fichier.
  • Limiter la zone de remplacement avec before: et after:.
  • Distinguer replace (modifie un motif) de lineinfile (modifie une ligne entière).
  • Utiliser des groupes de capture pour préserver une partie du motif d'origine.
  • Diagnostiquer une idempotence cassée par un double run.
  • Bases Ansible (cf. Premiers pas).
  • Notions de regex Python (les regex sont du re Python, pas du POSIX strict).
  • Un fichier cible avec des motifs à remplacer (config, JSON, YAML…).

L'usage de base, substituer toutes les occurrences

Section intitulée « L'usage de base, substituer toutes les occurrences »
- name: Mettre à jour l'URL de l'API dans tous les services
ansible.builtin.replace:
path: /etc/myapp/services.yml
regexp: 'https://api-old\.example\.com'
replace: 'https://api.example.com'

Comportement :

  • Toutes les occurrences du motif https://api-old.example.com dans le fichier sont remplacées.
  • Si aucune occurrence n'est trouvée → task ok (idempotent).
  • Si le fichier n'existe pas → task échoue (pas de create: dans replace).

C'est le contraste fondamental avec lineinfile : replace ne sait pas ajouter une ligne, il modifie ce qui existe déjà.

C'est la confusion la plus fréquente. Mémorisez ce tableau :

Aspectlineinfilereplace
GranularitéUne ligne entièreUn motif (peut être partie de ligne)
Cas d'usageAjouter/modifier/supprimer une ligne identifiéeSubstituer un motif partout dans le fichier
Si motif absentAjoute la ligne (par défaut)Ne fait rien (idempotent)
Multi-occurrencesUne seule ligne traitée par runToutes les occurrences traitées
Multi-lignesPas supportéSupporté via (?s) ou multiline: false
Regex stylePython re simplifiéPython re complet

Règle de décision :

  • « Je veux que cette ligne existe avec cette valeur » → lineinfile.
  • « Je veux remplacer ce motif partout » → replace.

Pour ne remplacer que dans une section d'un fichier, utiliser before (limite supérieure) et after (limite inférieure) :

- name: Activer SSL UNIQUEMENT dans la section [server]
ansible.builtin.replace:
path: /etc/myapp/config.ini
after: '^\[server\]' # commence à matcher après cette ligne
before: '^\[' # arrête au début de la section suivante
regexp: '^ssl_enabled\s*=\s*false'
replace: 'ssl_enabled = true'

Le bloc actif est entre after (exclu) et before (exclu). Si une seule des deux est définie, l'autre prend respectivement le début ou la fin du fichier.

Comme avec lineinfile, on peut utiliser des backrefs Python \g<1>, \g<2> pour conserver une partie du motif :

- name: Bumper la version d'un paquet npm dans package.json
ansible.builtin.replace:
path: /opt/app/package.json
regexp: '"express":\s*"\^?\d+\.\d+\.\d+"'
replace: '"express": "^4.18.2"'
# Ou avec capture pour préserver le préfixe
- name: Forcer toutes les URL http vers https en gardant le path
ansible.builtin.replace:
path: /etc/myapp/links.txt
regexp: 'http://(api\.example\.com)(/[^\s]*)?'
replace: 'https://\g<1>\g<2>'

Avec replace, contrairement à lineinfile, pas besoin de backrefs: true, c'est le comportement par défaut.

Comme lineinfile, le module accepte un validator pour ne pas écrire un fichier syntaxiquement cassé :

- name: Désactiver SELinux uniquement si le validator passe
ansible.builtin.replace:
path: /etc/selinux/config
regexp: '^SELINUX=\w+'
replace: 'SELINUX=permissive'
validate: "/usr/sbin/sestatus -v %s || /bin/true"

%s est remplacé par le chemin temporaire candidat. Le validator doit retourner 0 pour qu'Ansible commit le fichier.

Par défaut, le regex Python . ne matche pas les nouvelles lignes. Pour des remplacements multi-lignes :

# Remplacer un bloc multi-lignes (?s) = DOTALL
- name: Réinitialiser une section JSON
ansible.builtin.replace:
path: /etc/myapp/config.json
regexp: '(?s)"servers":\s*\[.*?\]'
replace: '"servers": []'

(?s) active le mode DOTALL, . matche aussi \n. Combiné avec *? (non gourmand), on peut remplacer un bloc complet sans risquer de manger trop loin.

Comme tout module modificateur de fichier, replace doit être idempotent :

Fenêtre de terminal
# 1er run : `changed` si le motif est trouvé et remplacé
ansible-playbook update-urls.yml
# 2e run immédiat : DOIT être `ok` (le motif a disparu après le 1er run)
ansible-playbook update-urls.yml

Si le 2e run rapporte encore changed, c'est que la substitution n'a pas fait disparaître le motif, typiquement parce que regexp matche aussi le résultat de replace :

# ❌ NON-IDEMPOTENT : chaque run ajoute encore "https://"
- name: Ajuster links.txt
ansible.builtin.replace:
path: /etc/myapp/links.txt
regexp: 'http'
replace: 'https://http'
# ✓ IDEMPOTENT : la regex ne match plus après remplacement
- name: Ajuster links.txt
ansible.builtin.replace:
path: /etc/myapp/links.txt
regexp: 'http://'
replace: 'https://'

Règle de design : la regex doit matcher uniquement la valeur d'origine, pas la valeur d'arrivée.

Les épreuves RHCSA et RHCE demandent souvent de modifier une valeur déjà présente dans un fichier système, sans le réécrire. Les trois exemples ci-dessous couvrent ce geste : une ancre ^ sur le nom du paramètre, une classe de caractères qui désigne l'ancienne valeur, et la nouvelle en clair. Le premier montre aussi ce qu'un examen peut demander sans que ce soit une bonne pratique en production : désactiver SELinux règle un symptôme et retire une protection, un module sefcontext ou seboolean traite la cause.

# Désactiver SELinux à chaud (config avant reboot)
- name: Ajuster config
ansible.builtin.replace:
path: /etc/selinux/config
regexp: '^SELINUX=\w+'
replace: 'SELINUX=disabled'
# Changer le default runlevel systemd
- name: Ajuster default.target
ansible.builtin.replace:
path: /etc/systemd/system/default.target
regexp: 'multi-user\.target'
replace: 'graphical.target'
# Remplacer toutes les références à un domaine déprécié
- name: Ajuster hosts
ansible.builtin.replace:
path: /etc/hosts
regexp: 'old\.corp\.local'
replace: 'new.corp.local'
# Forcer un timeout dans tous les fichiers d'un dossier (loop)
- name: Ajuster le fichier de configuration
ansible.builtin.replace:
path: "{{ item }}"
regexp: 'timeout\s*=\s*\d+'
replace: 'timeout = 30'
loop:
- /etc/myapp/conf.d/db.conf
- /etc/myapp/conf.d/cache.conf
- /etc/myapp/conf.d/api.conf

Quatre des sept lignes portent sur l'expression régulière, ce qui n'a rien d'étonnant pour un module dont c'est l'unique moyen de désigner ce qu'il modifie. La première est la plus retorse : une regex qui matche encore son propre résultat produit un changed perpétuel, alors même que le fichier est déjà dans l'état voulu. Les trois dernières rappellent les limites du module, il ne crée pas le fichier absent, ne traverse les sauts de ligne qu'avec (?s), et lit tout en UTF-8.

SymptômeCauseSolution
changed à chaque runLa regex matche aussi le résultatConcevoir la regex pour ne plus matcher après remplacement
Trop de remplacementsRegex gourmande (.*)Utiliser non-gourmand (.*?) ou ancres ^/$
Aucun remplacement alors que le motif existeCaractères spéciaux non échappésÉchapper ., \, (, ), [, ], etc.
replace ne traverse pas les newlinesMode DOTALL non activéPréfixer la regex avec (?s)
Service pas reload après modificationnotify: manquantAjouter notify: + handler
Fichier inexistant, task failreplace ne crée pasUtiliser copy avec force: false ou template à la place
Encoding cassé sur fichier non-UTF8replace lit en UTF-8 par défautPour les fichiers latin1 ou autre, considérer script ou command avec sed

Vérifiez que l'essentiel de ce guide est acquis. Les questions portent uniquement sur ce qui vient d'être expliqué ici.

Contrôle de connaissances

Validez vos connaissances avec ce quiz interactif

6 questions
6 min.
70% requis

Informations

  • Le chronomètre démarre au clic sur Démarrer
  • Questions à choix multiples, vrai/faux et réponses courtes
  • Vous pouvez naviguer entre les questions
  • Les résultats détaillés sont affichés à la fin

Lance le quiz et démarre le chronomètre

  • replace substitue un motif partout dans le fichier, pas un seul cas.
  • N'ajoute jamais de ligne, utiliser lineinfile pour ça.
  • before et after limitent la zone de remplacement à une section identifiée par regex.
  • (?s) active DOTALL pour les remplacements multi-lignes.
  • Toujours utiliser *? non gourmand sauf intention contraire.
  • La regex ne doit plus matcher après remplacement, sinon idempotence cassée.
  • validate: disponible comme dans lineinfile pour les fichiers critiques.
  • Pas de create:, le fichier doit exister.

Une substitution globale devient dangereuse dès que le fichier comporte plusieurs sections. Ce lab part d'un /etc/myapp.conf découpé en [server] et [client] : vous y remplacez une URL sur tout le fichier, activez une option dans la seule section [server] grâce à before: et after:, puis changez un numéro de port via un groupe de capture. La correction contrôle que [client] est resté intact et que le second passage ne change plus rien.

  • Module yum_repository : un dépôt se déclare avec son module, pas par substitution dans un fichier .repo.
  • Module sudoers : une règle sudo validée par visudo, bien plus sûre qu'une expression régulière sur /etc/sudoers.
  • Module sysctl : modifier un paramètre kernel sans réécrire sysctl.conf à coups de regex.

Ce site vous est utile ?

Sachez que moins de 1% des lecteurs soutiennent ce site.

Je maintiens ce site gratuitement, sans publicité, sans profilage et sans compte à créer. Un soutien, même symbolique, m'aide à couvrir l'hébergement et à garder ces ressources gratuites. Merci pour votre appui.

Le formulaire ne s'affiche pas ? Ouvrir Ko-fi dans un onglet.

Abonnez-vous et suivez mon actualité DevSecOps sur LinkedIn